<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="/feed-en.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<title>Rotatio (en)</title>
	<subtitle>Raffael Jesche ist Künstler, Webentwickler und immer auf der Suche nach neuen Herausforderungen.</subtitle>
	<link rel="self" type="application/atom+xml" href="https://rotatio.de/feed_en.xml" />
	<link rel="alternate" type="text/html" hreflang="en" href="https://rotatio.de/" />
	<link rel="alternate" type="text/html" hreflang="en" href="https://rotatio.de/home/" />
	<updated>2025-11-18T15:57:44Z</updated>
	<id>https://rotatio.de/</id>
	<author>
		<name>Raffael Jesche</name>
	</author>
	<entry xml:lang="en">
		<title>Multiple domains, rewrite rules and localhost</title>
		<link rel="alternate" type="text/html" hreflang="en" href="https://rotatio.de/notizen/domain-redirect-htaccess/" />
		<published>2025-11-18T20:10:35Z</published>
		<updated>2026-03-30T12:41:21Z</updated>
		<id>https://rotatio.de/notizen/domain-redirect-htaccess/</id>
		<content type="html">
			&lt;p&gt;So yesterday I finally published this shiny, new website. After weeks of work to tell 11ty to match (most of) my expactations it felt like a relief. But a new problem came up: redirecting some of my other domains to this site.&lt;/p&gt;
&lt;p&gt;As mentioned in &lt;a href=&quot;/notizen/neuer-blog-neues-glueck-mit-11ty/&quot;&gt;my last post (de)&lt;/a&gt;, I have a small collection of domains. One is only used for mails: &lt;code&gt;raffaeljesche.de&lt;/code&gt;. Another one from my first website &lt;code&gt;verstyler.de&lt;/code&gt;, abandoned for a few years and re-acquired recently, collects dust, too. Both should redirect to &lt;code&gt;rotatio.de&lt;/code&gt; now.&lt;/p&gt;
&lt;p&gt;The following text is very technical. I expect you to know about the basics of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html&quot;&gt;Apache mod_rewrite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Domain_Name_System&quot;&gt;Domain Name System (DNS)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://letsencrypt.org/&quot;&gt;Let’s Encrypt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_HTTP_status_codes&quot;&gt;HTTP status codes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now let’s move on (if you’re still interested).&lt;/p&gt;
&lt;h2 id=&quot;options&quot; tabindex=&quot;-1&quot;&gt;Options&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.inwx.de/&quot;&gt;INWX&lt;/a&gt; url redirect in DNS settings
&lt;ul&gt;
&lt;li&gt;This is not real DNS. It just looks like it in the user interface.&lt;/li&gt;
&lt;li&gt;Causes missing SSL certificate issues when typing &lt;code&gt;https://example.com&lt;/code&gt; (with s) directly in the browser address bar. Just typing &lt;code&gt;example.com&lt;/code&gt; without protocol hides this problem, because the first redirect on an INWX server is unencrypted.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;point domains (with and without &lt;code&gt;www.&lt;/code&gt;) to &lt;a href=&quot;https://uberspace.de/&quot;&gt;Uberspace&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Uberspace takes care of Let’s Encrypt certificates automatically (&lt;a href=&quot;https://manual.uberspace.de/web-https/&quot;&gt;Uberspace docs: https&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;causes duplicated content if not redirected properly&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;my-old-strategy&quot; tabindex=&quot;-1&quot;&gt;My old strategy&lt;/h2&gt;
&lt;p&gt;Uberspace has one real document root in &lt;code&gt;/var/www/virtual/$USER/html/&lt;/code&gt;. By default, all domains point to this directory. If a folder &lt;code&gt;/var/www/virtual/$USER/example.com&lt;/code&gt; exists, the site is served from over there. &lt;code&gt;$_SERVER[&#39;DOCUMENT_ROOT&#39;]&lt;/code&gt; still points to the real document root &lt;code&gt;/var/www/virtual/$USER/html/&lt;/code&gt;. This can cause issues for some applications and may need a &lt;code&gt;RewriteBase /&lt;/code&gt; in &lt;code&gt;.htaccess&lt;/code&gt;, but all my files are static html files, so I don’t care. Apache can take care of the domain redirects by adding a &lt;code&gt;.htaccess&lt;/code&gt; to all (sub) domain directories on my Uberspace.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.htaccess&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class=&quot;language-apacheconf&quot;&gt;&lt;code class=&quot;language-apacheconf&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# redirect all other domains (e. g. $USER.uber.space, www subdomain)&lt;/span&gt;
&lt;span class=&quot;token directive-inline property&quot;&gt;RewriteEngine&lt;/span&gt; On
&lt;span class=&quot;token directive-inline property&quot;&gt;RewriteCond&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;%{HTTP_HOST}&lt;/span&gt; !=rotatio.de
&lt;span class=&quot;token directive-inline property&quot;&gt;RewriteRule&lt;/span&gt; (.*) https://rotatio.de/&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt; &lt;span class=&quot;token directive-flags keyword&quot;&gt;[R=301,L]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Folder setup:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# https://rotatio.de (synced with 11ty output directory)
/var/www/virtual/$USER/html/

# https://www.rotatio.de
/var/www/virtual/$USER/www.rotatio.de/.htaccess

# https://rotatio.uber.space (default Uberspace user domain)
/var/www/virtual/$USER/rotatio.uber.space/.htaccess
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This setup can be simplified by creating only one folder and creating symlinks for all domains to that directory. This strategy is nice, because I don’t have do deal with modifying &lt;code&gt;.htaccess&lt;/code&gt; files provided by a CMS (which might change with an update unexpectedly).&lt;/p&gt;
&lt;p&gt;When pointing some more (outdated) domains to this setup, it becomes messy:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# https://raffaeljesche.de
/var/www/virtual/$USER/raffaeljesche.de/.htaccess

# https://www.raffaeljesche.de
/var/www/virtual/$USER/www.raffaeljesche.de/.htaccess

# https://verstyler.de
/var/www/virtual/$USER/verstyler.de/.htaccess

# https://www.verstyler.de
/var/www/virtual/$USER/www.verstyler.de/.htaccess
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I don’t use a CMS and I have full control on my settings. So I thought, I could skip the manual process of creating all these empty folder duplicates, that only exist for redirecting.&lt;/p&gt;
&lt;h2 id=&quot;the-problem-with-.htaccess&quot; tabindex=&quot;-1&quot;&gt;The problem with &lt;code&gt;.htaccess&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;It’s complicated.&lt;/p&gt;
&lt;p&gt;Sadly, while researching, Cloudflare was down (again) and with it half of the internet. Multiple search results weren’t accessible and the &lt;a href=&quot;https://htaccess.madewithlove.com/&quot;&gt;htaccess testing tool&lt;/a&gt; suddenly stopped working because it’s API requests were down too.&lt;/p&gt;
&lt;p&gt;Some useful information from a &lt;a href=&quot;https://stackoverflow.com/questions/3184018/htaccess-rewritecond-without-messing-up-localhost#answer-19644124&quot;&gt;Stack Overflow post&lt;/a&gt; didn’t work for me, but at least their server was online. And I found another thread on Stack Overflow with many tips for &lt;a href=&quot;https://stackoverflow.com/questions/9153262/tips-for-debugging-htaccess-rewrite-rules&quot;&gt;debugging &lt;code&gt;.htaccess&lt;/code&gt; files&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Luckily I remembered, that I solved this problem in the past already–and I found a note from 2020. It needed some refactoring, but it showed me the missing piece: I forgot to match the port number while testing. So checking against &lt;code&gt;localhost&lt;/code&gt; etc. always failed while running on &lt;code&gt;http://localhost:8080&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Changes to my old note:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;don’t use 301 for all domains, only for www subdomain&lt;/li&gt;
&lt;li&gt;use 302 redirect instead for other domains&lt;/li&gt;
&lt;li&gt;check against &lt;code&gt;127.x.x.x&lt;/code&gt; range instead of &lt;code&gt;127.0.0.1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;added check against &lt;code&gt;192.168.x.x&lt;/code&gt; range&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The final config:&lt;/p&gt;
&lt;pre class=&quot;language-apacheconf&quot;&gt;&lt;code class=&quot;language-apacheconf&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;IfModule&lt;/span&gt;&lt;span class=&quot;token directive-block-parameter attr-value&quot;&gt; mod_rewrite.c&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token directive-inline property&quot;&gt;RewriteEngine&lt;/span&gt; on

	&lt;span class=&quot;token comment&quot;&gt;# Redirect all other domains (e. g. $USER.uber.space, www subdomain)&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;# Checking for HTTPS or port 443 is not needed, because Uberspace doesn&#39;t&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;# support unencrypted connections and they renew Let&#39;s Encrypt certificates&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;# automatically.&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;# 301 for www subdomain&lt;/span&gt;
	&lt;span class=&quot;token directive-inline property&quot;&gt;RewriteCond&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;%{HTTP_HOST}&lt;/span&gt; =www.rotatio.de
	&lt;span class=&quot;token directive-inline property&quot;&gt;RewriteRule&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt; ^(.*)$&lt;/span&gt; https://rotatio.de/&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt; &lt;span class=&quot;token directive-flags keyword&quot;&gt;[R=301,L]&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;# 302 for all other domains pointing to this server&lt;/span&gt;
	&lt;span class=&quot;token directive-inline property&quot;&gt;RewriteCond&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;%{HTTP_HOST}&lt;/span&gt; !=rotatio.de
	&lt;span class=&quot;token comment&quot;&gt;# but not on localhost (with optional port)&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;# The following conditions could be merged into one unreadable regex.&lt;/span&gt;
	&lt;span class=&quot;token directive-inline property&quot;&gt;RewriteCond&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;%{HTTP_HOST}&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt; !^localhost(?::&#92;d+)?$&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;# still on localhost, but covering the whole 127.x.x.x range&lt;/span&gt;
	&lt;span class=&quot;token directive-inline property&quot;&gt;RewriteCond&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;%{HTTP_HOST}&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt; !^127&#92;.[0-9]{1,3}&#92;.[0-9]{1,3}&#92;.[0-9]{1,3}(?::&#92;d+)?$&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;# and not while exposing dev machine to (W)LAN for cross device testing&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;# covering 192.168.x.x range&lt;/span&gt;
	&lt;span class=&quot;token directive-inline property&quot;&gt;RewriteCond&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;%{HTTP_HOST}&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt; !^192&#92;.168&#92;.[0-9]{1,3}&#92;.[0-9]{1,3}(?::&#92;d+)?$&lt;/span&gt;
	&lt;span class=&quot;token directive-inline property&quot;&gt;RewriteRule&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt; ^(.*)$&lt;/span&gt; https://rotatio.de/&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt; &lt;span class=&quot;token directive-flags keyword&quot;&gt;[R=302,L]&lt;/span&gt;
&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;IfModule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now my document roots are clean. All domains point to the &lt;code&gt;html/&lt;/code&gt; directory and are handled with a single &lt;code&gt;.htaccess&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Nice!&lt;/p&gt;
&lt;p&gt;Of course, the whole file is a bit longer. You can inspect &lt;a href=&quot;https://codeberg.org/raffaelj/rotatio.de-11ty/src/branch/main/content/.htaccess&quot;&gt;the &lt;code&gt;.htaccess&lt;/code&gt; source code on Codeberg&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And don’t forget: Never set a 301 redirect before all your tests are finished.&lt;/p&gt;

		</content>
	</entry>
</feed>
