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.com
should serve the same content as www.foo.com
- fallback for unknown domains to some sort of default
- fallback for unknown subdomains of
foo.com
to www.foo.com
unless the www
is 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 [email protected]
<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 the ServerAlias
are just wildcards.
- The
%n
in the VirtualDocumentRoot
are from the document name interpolation.
- The
%n
in the second RewriteCond
refer to the selections (.*)
from the first RewriteCond
, eg. the parts of the requested domain.
- The
%n
in the RewriteRule
do too.
- The
$1
in the RewriteRule
refers to the selection (.*)
at the beginning of the RewriteRule
. Which captures everything from the domain till the ?
in the request url. Any querystring is automatically added to the url by mod_proxy
.