Multiple domains, rewrite rules and localhost
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.
As mentioned in my last post (de), I have a small collection of domains. One is only used for mails: raffaeljesche.de. Another one from my first website verstyler.de, abandoned for a few years and re-acquired recently, collects dust, too. Both should redirect to rotatio.de now.
The following text is very technical. I expect you to know about the basics of:
Now let’s move on (if you’re still interested).
Options
- INWX url redirect in DNS settings
- This is not real DNS. It just looks like it in the user interface.
- Causes missing SSL certificate issues when typing
https://example.com(with s) directly in the browser address bar. Just typingexample.comwithout protocol hides this problem, because the first redirect on an INWX server is unencrypted.
- point domains (with and without
www.) to Uberspace- Uberspace takes care of Let’s Encrypt certificates automatically (Uberspace docs: https
- causes duplicated content if not redirected properly
My old strategy
Uberspace has one real document root in /var/www/virtual/$USER/html/. By default, all domains point to this directory. If a folder /var/www/virtual/$USER/example.com exists, the site is served from over there. $_SERVER['DOCUMENT_ROOT'] still points to the real document root /var/www/virtual/$USER/html/. This can cause issues for some applications and may need a RewriteBase / in .htaccess, but all my files are static html files, so I don’t care. Apache can take care of the domain redirects by adding a .htaccess to all (sub) domain directories on my Uberspace.
.htaccess file:
# redirect all other domains (e. g. $USER.uber.space, www subdomain)
RewriteEngine On
RewriteCond %{HTTP_HOST} !=rotatio.de
RewriteRule (.*) https://rotatio.de/$1 [R=301,L]
Folder setup:
# 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
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 .htaccess files provided by a CMS (which might change with an update unexpectedly).
When pointing some more (outdated) domains to this setup, it becomes messy:
# 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
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.
The problem with .htaccess
It’s complicated.
Sadly, while researching, Cloudflare was down (again) and with it half of the internet. Multiple search results weren’t accessible and the htaccess testing tool suddenly stopped working because it’s API requests were down too.
Some useful information from a Stack Overflow post didn’t work for me, but at least their server was online. And I found another thread on Stack Overflow with many tips for debugging .htaccess files.
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 localhost etc. always failed while running on http://localhost:8080.
Changes to my old note:
- don’t use 301 for all domains, only for www subdomain
- use 302 redirect instead for other domains
- check against
127.x.x.xrange instead of127.0.0.1 - added check against
192.168.x.xrange
The final config:
<IfModule mod_rewrite.c>
RewriteEngine on
# Redirect all other domains (e. g. $USER.uber.space, www subdomain)
# Checking for HTTPS or port 443 is not needed, because Uberspace doesn't
# support unencrypted connections and they renew Let's Encrypt certificates
# automatically.
# 301 for www subdomain
RewriteCond %{HTTP_HOST} =www.rotatio.de
RewriteRule ^(.*)$ https://rotatio.de/$1 [R=301,L]
# 302 for all other domains pointing to this server
RewriteCond %{HTTP_HOST} !=rotatio.de
# but not on localhost (with optional port)
# The following conditions could be merged into one unreadable regex.
RewriteCond %{HTTP_HOST} !^localhost(?::\d+)?$
# still on localhost, but covering the whole 127.x.x.x range
RewriteCond %{HTTP_HOST} !^127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}(?::\d+)?$
# and not while exposing dev machine to (W)LAN for cross device testing
# covering 192.168.x.x range
RewriteCond %{HTTP_HOST} !^192\.168\.[0-9]{1,3}\.[0-9]{1,3}(?::\d+)?$
RewriteRule ^(.*)$ https://rotatio.de/$1 [R=302,L]
</IfModule>
Now my document roots are clean. All domains point to the html/ directory and are handled with a single .htaccess file.
Nice!
Of course, the whole file is a bit longer. You can inspect the .htaccess source code on Codeberg.
And don’t forget: Never set a 301 redirect before all your tests are finished.