It has been almost five years since I wrote an answer to a serverfault question about dynamic vhosting with fallback using Apache. I repost it here on my very own blog, for historic reasons.
RemyNL answered:
I also came across this question googling for apache2 dynamic vhost fallback and Luc’s answer helped me a lot with solving my problem, but I still want to show what I did to achieve my goals, mainly because it involved some extra works and because I think it could be helpful for any future googlers…
My goals
- dynamic vhosting for all domains and subdomains pointing at my VPS
foo.comshould serve the same content aswww.foo.com- fallback for unknown domains to some sort of default
- fallback for unknown subdomains of
foo.comtowww.foo.comunless thewwwis not available, fallback to default instead
DNS
I have a couple of domains (and all their subdomains) pointing at my VPS, for example:
- foo.com
- bar.com
- foobar.com
Filesystem
I have the following directories, domains contain directories with the names of the available subdomains, the www directory is required, but the config should be able to deal with the situation where it is not present. Localhost is used as default fallback:
/var
/www
/localhost
/foo.com
/www
/bar
/bar.com
/foo
Tests
Translating my goals into testable cases:
- foo.com should be served from foo.com/www
- www.foo.com should be served from foo.com/www
- bar.foo.com should be served from foo.com/bar
- foo.foo.com should be served from foo.com/www (foo.com/foo does not exist)
- bar.com should be served from localhost (bar.com/www does not exist)
- www.bar.com should be served from localhost (bar.com/www does not exist)
- foo.bar.com should be served from bar.com/foo
- bar.bar.com should be served from localhost (bar.com/bar does not exist)
- foobar.com should be served from localhost (foobar.com does not exist)
- www.foobar.com should be served from localhost (foobar.com does not exist)
- foo.foobar.com should be served from localhost (foobar.com does not exist)
The Solution
This uses: mod_rewrite, mod_proxy_http and ofcourse mod_vhost_alias.
ServerName my.domain
ServerAdmin admin@my.domain
<VirtualHost *:80>
ServerName localhost
VirtualDocumentRoot /var/www/localhost
</VirtualHost>
<VirtualHost *:80>
ServerName sub.domain
ServerAlias *.*.*
VirtualDocumentRoot /var/www/%-2.0.%-1.0/%-3
RewriteEngine on
RewriteCond %{HTTP_HOST} ^(.*)\.(.*)\.(.*)$ [NC]
RewriteCond /var/www/%2.%3 !-d
RewriteRule (.*) http://localhost/$1 [P]
RewriteCond %{HTTP_HOST} ^(.*)\.(.*)\.(.*)$ [NC]
RewriteCond /var/www/%2.%3/%1 !-d
RewriteCond /var/www/%2.%3/www !-d
RewriteRule (.*) http://localhost/$1 [P]
RewriteCond %{HTTP_HOST} ^(.*)\.(.*)\.(.*)$ [NC]
RewriteCond /var/www/%2.%3/%1 !-d
RewriteRule (.*) http://%2.%3/$1 [P]
</VirtualHost>
<VirtualHost *:80>
ServerName bare.domain
ServerAlias *.*
VirtualDocumentRoot /var/www/%-2.0.%-1.0/www
RewriteEngine on
RewriteCond %{HTTP_HOST} ^(.*)\.(.*)$ [NC]
RewriteCond /var/www/%1.%2 !-d [OR]
RewriteCond /var/www/%1.%2/www !-d
RewriteRule (.*) http://localhost/$1 [P]
</VirtualHost>
How does this work? There are three virtual hosts defined:
localhost
The localhost serves as a default. All requests that are not resolvable are served by localhost. Setting up a symlink from localhost to any of your domains is like setting up that site as a default.
sub.domain
The sub.domain vhost is taking all requests in the form of *.*.*. By default all requests are served from /domain.com/sub as defined by VirtualDocumentRoot /var/www/%-2.0.%-1.0/%-3.
fallback:
The first RewriteRule takes care of unknown domains, eg. domain.com directory does not exist, by proxying the localhost website.
The second RewriteRule also proxies to localhost when both the domain.com/sub and the domain.com/www directories are not present.
The third RewriteRule proxies to domain.com when domain.com/sub does not exist. We know domain.com/www does exist because of the second rewrite block.
bare.domain
The bare.domain vhost is taking the *.*requests and serves them /domain.com/www
Here the RewriteRule will proxy to localhost when domain.com or domain.com/www do not exist.
^$%.*!!!
I had some trouble wrapping my head around all those $ and % signs in the RewriteCond and RewriteRule so I will explain about them here:
ServerAlias *.*.*
VirtualDocumentRoot /var/www/%-2.0.%-1.0/%-3
RewriteCond %{HTTP_HOST} ^(.*)\.(.*)\.(.*)$ [NC]
RewriteCond /var/www/%2.%3/%1 !-d
RewriteRule (.*) http://%2.%3/$1 [P]
- The
*in theServerAliasare just wildcards. - The
%nin theVirtualDocumentRootare from the document name interpolation. - The
%nin the secondRewriteCondrefer to the selections(.*)from the firstRewriteCond, eg. the parts of the requested domain. - The
%nin theRewriteRuledo too. - The
$1in theRewriteRulerefers to the selection(.*)at the beginning of theRewriteRule. Which captures everything from the domain till the?in the request url. Any querystring is automatically added to the url bymod_proxy.
