Apache Dynamically Configure and Create Multiple Virtual Hosts with mod_vhost_alias
If you ever find yourself repeating the same set of virtual hosts configuration with only minimal differences in directives like
Directory then there are some ways to reduce the configuration code. Whether you are serving multiple domains or subdomains in Apache HTTPD Web Server, there are a couple of ways to dynamically generate virtual hosts to avoid writing lots of same boilerplate configuration code.
mod_macro module is one good solution for instance, that allows us to write less and do more with the help of templates. The problem we just described, can be solved with
mod_macro perfectly. But in this article we’ll discuss another module called
mod_vhost_alias to solve the same problem.
mod_vhost_alias module allows us to create multiple arbitrary number of virtual hosts dynamically. What this means is that we won’t have to configure multiple virtual hosts by writing one block for each of them, as long as they are mostly similar. Let’s look at an example of multiple virtual hosts for different domains and subdomains:
<VirtualHost *:80> ServerName foo.example.com DocumentRoot /var/www/foo.example.com/html </VirtualHost> <VirtualHost *:80> ServerName bar.example.com DocumentRoot /var/www/bar.example.com/html </VirtualHost> <VirtualHost *:80> ServerName bobdob.com DocumentRoot /var/www/bobdob.com/html </VirtualHost>
Generally you’d have a few other directives like
CustomLog, etc. but you can already see that there’s a pattern forming across all the virtual host blocks. Apart from certain differences (server name and its usage in document root) that we should be able to set dynamically, they’re mostly similar. Imagine if we had 10 other such virtual hosts, that’d be a lot of repetition. Let’s see how we can keep it short and simple with
Note: You’ll need to enable the module first. Since I’m on Ubuntu I’ll just do an
Once the module is enabled, all we have to do is replace the blocks above with this:
That’s it! Don’t forget to restart the server.
We can put the above line in our main config (
apache2.conf) inside or outside of a
<VirtualHost> container. In this case, it’s outside. Putting it inside a
<VirtualHost> will have a different behaviour, we’ll discuss that in a bit.
VirtualDocumentRoot directive is similar to
DocumentRoot in terms of defining the root folder path from where Apache will serve files. The difference is that
DocumentRoot comes from the
core module where as
VirtualDocumentRoot comes from
mod_vhost_alias allowing us to configure for dynamic mass virtual hosting.
One interesting thing you’ll notice is the
%0. That is an interpolation token or specifier similar to
printf function if you’re familiar with that.
%0 gets interpolated with the canonical name for the server or the self-referencing URL. This canonical name or self-referential URL is determined by the
UseCanonicalName directive which is
Off by default which means the value for
%0 will resolve to the client request’s
Host header (hostname and port). So for a bunch of request URLs, lets see what the document root and the file path served will look like:
URL: http://foo.example.com/directory/file.html Document root: /var/www/foo.example.com/html Document/file: /var/www/foo.example.com/html/directory/file.html URL: http://bar.example.com/directory/file.html Document root: /var/www/bar.example.com/html Document/file: /var/www/bar.example.com/html/directory/file.html URL: http://bobdob.com/directory/file.html Document root: /var/www/bobdob.com/html Document/file: /var/www/bobdob.com/html/directory/file.html
Any instance of
VirtualDocumentRoot in the main server config means the matching of a request to the document root is handled by
mod_vhost_alias and not the core module of Apache. This means that
VirtualDocumentRoot in the main server config will disable any other
<VirtualHost> container defined.
VirtualDocumentRoot were defined inside another
<VirtualHost>, that would not happen. The virtual host request matching would happen the default way. But that would lead to another challenge where for name-based virtual hosts, the first name-based virtual host will get picked up (default virtual host) if no server name match is found. So you’ll have to make sure the
VirtualDocumentRoot virtual host block is the first virtual host in appearance order. This won’t be a problem for IP-based virtual hosts. Let’s look at an example to understand better:
<VirtualHost *:80> ServerName foo.example.com ServerAdmin [email protected] DocumentRoot /var/www/foo.example.com/html </VirtualHost> <VirtualHost *:80> VirtualDocumentRoot /var/www/%0/html </VirtualHost>
If our configuration looks like above, then pretty much for all
*.example.com subdomains or other domains, the first virtual host will always be picked up since its the default named vhost. To fix this we’ll have to make sure the second block appears before the first one. But for IP-based virtual hosts, this won’t be a problem:
<VirtualHost 188.8.131.52:80> ServerName foo.example.com ServerAdmin [email protected] DocumentRoot /var/www/foo.example.com/html </VirtualHost> <VirtualHost 184.108.40.206:80> VirtualDocumentRoot /var/www/%0/html </VirtualHost>
Since the virtual host will be matched or picked for different IP connections (of the server) altogether, serving of one over the other is never a problem.
What if we use
If we did something like this:
<VirtualHost *:80> ServerName foo.example.com VirtualDocumentRoot /var/www/%0/html </VirtualHost>
ServerName would do its job in the request matching process, but the interpolated value for
%0 will continue to depend upon
UseCanonicalName. This means if
UseCanonicalName is set to
Off then the request’s
Host header value will be used for
%0 interpolation. And if it is set to
DNS then reverse DNS lookup will happen on the server’s IP on which the client connection is received. This reverse lookup hostname will be eventually use to interpolate
Is %0 the only interpolation specifier I can use ?
Nope, there are quite a few. Let’s go through them. First let’s go through the formats allowed:
%%– Inserts a
%p– Inserts the port number of the canonical server name or the self-referential URL resolved based on
%N.M– Inserts the whole or part of the hostname from the canonical server name (or the self-referential URL).
From the third option above,
N represents the whole or part of the server name or IP address (we will see an example of IP address below) in the dotted format. The
M is optional and defaults to
M selects the character part from
N. In the
%0 specifier we’ve been using till now, it represents the
N and refers to the whole part or name of the server name. Hence, the entire server name or IP address is used for interpolation.
Following are all the different forms for the
|Value of N or M||Interpretation|
|the whole name|
|the first part|
|the second part|
|the last part|
|the penultimate part|
|the second and all subsequent parts|
|the penultimate and all preceding parts|
|the same as |
M is greater than the number of parts available a single underscore is interpolated.
For a request to
http://www.example.com/directory/file.html let’s see the different forms of interpolation for the document root:
VirtualDocumentRoot /var/www/%0 # Document Root: /var/www/www.example.com # Document/File: /var/www/www.example.com/directory/file.html VirtualDocumentRoot /var/www/%2+/%2.1/%2.2/%2.3/%2 # Document Root: /var/www/example.com/e/x/a/example # Document/File: /var/www/example.com/e/x/a/example/directory/file.html VirtualDocumentRoot /var/www/%2+/%2.-1/%2.-2/%2.-3/%2 # Document Root: /var/www/example.com/e/l/p/example # Document/File: /var/www/example.com/e/l/p/example/directory/file.html VirtualDocumentRoot /var/www/%2+/%2.1/%2.2/%2.3/%2.4+ # Document Root: /var/www/example.com/e/x/a/mple # Document/File: /var/www/example.com/e/x/a/mple/directory/file.html
Did you say something about IP Address interpolation above?
mod_vhost_alias module provides us with
VirtualDocumentRootIP directive that is the same, except it uses the IP address of the server on which the client connection is made, for the interpolation (instead of the server name). So if the connection is made on say
220.127.116.11, then this is how the interpolation would look like:
VirtualDocumentRootIP /var/www/%1/%2/%3/%4 # Document Root: /var/www/164/92/156/80
What are the other configuration directives provided by mod_vhost_alias?
VirtualDocumentRoot in the sense that it specifies the location that will be interpolated by Apache and used to find CGI scripts for execution (via
VirtualScriptAlias except that it uses IP address of the server on which the connection is made for interpolation instead of the server name. So in the interpolation sense, it is similar to
How will I know which request is being served for which host or server name in the logs?
The following format strings or interpolation specifiers in
LogFormat will help:
%A– Local IP Address
%V– The self-referential URL or server name (controlled via
What are the advantages and disadvantages of mass vhosting with mod_vhost_alias?
- Makes the configuration code for multiple virtual hosts smaller, easier to maintain.
- Adding a new virtual host or removing one doesn’t require re-configuring Apache and restarting it. It is now just a matter of creating the appropriate DNS entries for different hostnames and creating the appropriate document root folders on the machine.
- Different log files or varying configurations for each server name is not possible. If this must be achieved, then use multiple name-based virtual hosts as shown above. As far as log files are concerned, too many log files can breach the file descriptor limits anyway. You can split the log file into multiple copies based on the hostnames using the
Is there anything else that I should keep in mind ?
Just a few notes:
mod_userdirwill override the functionality of
DOCUMENT_ROOTenvironment variable passed to CGI scripts are set by the core module.
mod_vhost_aliasdoesn’t set any value in this variable. Hence any scripts depending upon
getenv('DOCUMENT_ROOT')may get a misleading value.