Home > Virtual Hosting | Japanese

Virtual Hosting

Contact

Dynamically Configured Virtual Hosting in Apache (Experiment)

Recently I moved KobuCom's web and mail servers to a cheaper VPS (virtual private server) and added free SSL certificates from Let's Encrypt. In doing so, I used methods called dynamically configured mass virtual hosting to simplify virtual host configuration in Apache web server. I like to share the experience here.

The number of domains I set up was just three. Even though I don't feel good when I repeat the same settings in more than one place. The dynamic methods are good for my mental health.

Three Methods

On searching for a way to avoid repetition of VirtualHost's, I found the following discussion:

Sharing configuration between several VHosts
https://stackoverflow.com/questions/18714293/sharing-configuration-between-several-vhosts

One answer says Apache proposes three methods to do it:

Module Description Difficulty Flexibility
mod_vhost_alias Variable path Simple Limited
mod_macro Macro expansion Relatively simple Not so limited
mod_rewrite URL rewriting Difficult Unlimited

Of these, I tested the first two. Eventually I used mod_vhost_alias for HTTP port 80 virtual hosting and mod_macro for HTTPS port 443 in my site.

I will show you in turn simple configuration examples used in my experiment. I tested with Apache2 v2.4.38 on Raspberry Pi's Raspbian (Debian 10) on a local lan. I assume the reader has knowledge of basic Apache configuration and virtual host settings (name-based vs IP-based, etc.). Related links in Apache are shown at the end of this page.

Static Configuration

I will use the following two domains in examples below. Name-based virtual hosting is used instead of IP-based.

A traditional static configuration is

<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com/html
    ScriptAlias "/cgi-bin/" "/var/www/example.com/cgi-bin/"
    # other settings
</VirtualHost>

<VirtualHost *:80>
    ServerName example.org
    ServerAlias www.example.org
    DocumentRoot /var/www/example.org/html
    ScriptAlias "/cgi-bin/" "/var/www/example.org/cgi-bin/"
    # other settings
</VirtualHost>

You must repeat VirtualHost settings the number of times of the domains served by Apache.

mod_vhost_alias

I first tried vhost_alias module. This module was already included in Debian 10 and CentOS 8. You have to enable it with "a2enmod" in Debian.

Here is an example of VirtualHost using the vhost_alias module.

<VirtualHost *:80>
  UseCanonicalName Off
  VirtualDocumentRoot "/var/www/%-2.0.%-1.0/html"
  VirtualScriptAlias "/var/www/%-2.0.%-1.0/cgi-bin/"
  # other common settings can be added here
</VirtualHost>

"UseCanonicalName" indicates how to determine a requested domain name. "Off" takes from "Host" header while "DNS" means a reverse DNS lookup. "Off" is for name-based virtual hosting. "DNS" is for IP-based hosting. "VirtualDocumentRoot" and "VirtualScriptAlias" specify locations where static or dynamic contents reside, respectively. In IP-based hosting, "VirtualDocumentRootIP" and "VirtualScriptAliasIP" are used instead. A portion dynamically determined on reception of a request is "%-2.0.%-1.0". This represents the last two parts of a domain name delimited by dots (.). The variable part will be "example.com" if a requested domain name is either "example.com" or "www.example.com". I planned to hit the same directory whether "www" is present or not.

Pattern syntax

The mod_vhost_alias's path component pattern syntax needs a little explanation. The apache document presents concise tables and some examples. I quote the tables.

A variable pattern can contain:

%% insert a %
%p insert the port number of the virtual host
%N.M insert (part of) the name

and both 'N' and 'M' takes the following values:

0 the whole name
1 the first part
2 the second part
-1 the last part
-2 the penultimate part
2+ the second and all subsequent parts
-2+ the penultimate and all preceding parts
1+ and -1+ the same as 0

Now let me show you some patterns using 'www.example.com' as an example:

Pattern Value Remark
%0 www.example.com whole domain name
%1 www fisrt dot-delimited component
%-1 com last component
%1.0 www whole string of the first component
%-1.0 com whole string of the last component
%-2.0 example whole string of the second last component
%1.1 w first character of the first component
%-1.-1 m last character of the last component

I suppose you now understand what '%-2.0.%-1.0' means. It consists of '%-2.0', literal dot and '%-1.0'. In this example, it represents 'example.com'.

Test

This is a directory I used for the test:

$pwd
/var/www

$ ls
example.com/  example.org/

$ ls -R example.com
example.com:
cgi-bin/  html/

example.com/cgi-bin:
test.cgi*

example.com/html:
index.html

Let me show you content of the files.

$cat example.com/html/index.html
example.com

$cat example.com/cgi-bin/test.cgi
#!/bin/bash
echo 'Content-Type: text/plain'
echo ''
echo test.cgi of example.com running

You see the same contents in the "example.org" directory except "com" is changed to "org". Let me test with HTML files.

$ curl http://example.com/
example.com
$ curl http://example.org/
example.org

Next, try CGI scripts.

$ curl http://example.com/cgi-bin/test.cgi
test.cgi of example.com running
$ curl http://example.org/cgi-bin/test.cgi
test.cgi of example.org running

Also try a case with "www" attached to the domain.

$ curl http://www.example.com/
example.com
$ curl http://www.example.com/cgi-bin/test.cgi
test.cgi of example.com running

It is easy to use vhost_alias module to configure dynamic virtual hosts. However, only changeable values are DocumentRoot and ScriptAlias. If you need other settings different per domain, mod_vhost_alias is not usable. The mod_macro module is more flexible.

mod_macro

The second method I tried is macro module. This module was already included in Debian 10 and CentOS 8. You have to enable it with "a2enmod" in Debian.

Here is an example of VirtualHost using mod_macro.

<Macro VHost $domain>
<VirtualHost *:80>
    ServerName $domain
    ServerAlias www.$domain
    DocumentRoot "/var/www/$domain/html"
    ScriptAlias "/cgi-bin/" "/var/www/$domain/cgi-bin/"
    # other per-domain settings can be added here
</VirtualHost>
</Macro>

Use VHost example.com
Use VHost example.org

UndefMacro VHost

A macro definition starts from "Macro VHost" and ends with "/Macro". The macro argument $domain indicates a variable domain name. "Use VHost" expands the macro into a settings having the value of $domain. In this case, two sets of VirtualHost blocks for "example.com" and "example.org" are created. "UndefMacro" indicates no more "Use" appears in the rest of the file.

Let me test.

$ curl www.example.com
example.com
$ curl www.example.org
example.org
$ curl example.com
example.com
$ curl example.org
example.org
$ curl example.com/cgi-bin/test.cgi
test.cgi of example.com running
$ curl example.org/cgi-bin/test.cgi
test.cgi of example.org running

The first introduced mod_vhost_alias is dynamic in real meaning. Without prior designation, any domain name published through your DNS can be a target of virtual hosting. In a mod_macro case, static settings for domains designated with "Use" are created during Apache startup. Other domains are not a target of virtual hosting.

mod_rewrite

The last method is to rewrite URL arbitrarily using the rewrite module. An apache document shows some examples of virtual hosting using mod_rewrite. I quote one example where site.example.com refers to /home/site/www.

# SITE.example.com -> /home/SITE/www
RewriteEngine on
RewriteMap    lowercase int:tolower
RewriteCond   %{HTTP_HOST} !^www\.
RewriteCond   ${lowercase:%{HTTP_HOST}}   ^([^.]+)\.example\.com$
RewriteRule   ^(.*)    /home/%1/www$1

This page also states:

mod_rewrite is usually not the best way to configure virtual hosts. You should first consider the alternatives before resorting to mod_rewrite.

I agree with this. Using mod_rewrite is difficult and requires lots of time and effort especially in debugging a script. It's hard to understand a script written by others or me six months ago. I am trying not to use it as possible.

About virtual hosting, I recommend first think if mod_vhost_alias can do and use mod_macro if not.

References

Dynamically Configured Mass Virtual Hosting
https://httpd.apache.org/docs/trunk/vhosts/mass.html

Apache Module mod_vhost_alias
http://httpd.apache.org/docs/2.4/mod/mod_vhost_alias.html

Apache Module mod_macro
http://httpd.apache.org/docs/2.4/mod/mod_macro.html

Dynamic mass virtual hosts with mod_rewrite
https://httpd.apache.org/docs/trunk/rewrite/vhosts.html

An In-Depth Discussion of Virtual Host Matching
https://httpd.apache.org/docs/2.4/vhosts/details.html

Notice

We are willing to do network infrastructure or site construction jobs in a remote style. Please contact.

Written 2020-May-03