ApacheVirtualHost

This page contains information on how to configure Virtual Hosts (or vhosts) for the Apache HTTP server. There is some general information about how vhosts work as well as concrete configuration documentation.

The main page about Apache configuration is this: Apache.


How Virtual Hosts work

The VirtualHost directive

Virtual hosts must be configured with VirtualHost directives. For instance

<VirtualHost 1.2.3.4>
[...]
</VirtualHost>

encloses a block of directives that are used when requests on the interface 1.2.3.4 need to be served. There may be several such blocks for 1.2.3.4, but each block must contain a different ServerName directive - this directive decides which block is used to service a specific request. Example:

<VirtualHost 192.168.178.20:80>
  ServerName www.herzbube.ch
  ServerAlias herzbube.ch 212.101.18.224
  [...]
</VirtualHost>

This block is used when a request for www.herzbube.ch, herzbube.ch or the explicit IP address 212.101.18.224 comes in from the interface 192.168.178.20, on port 80.

For every HTTP request, the HTTP header Host: is matched against the server name and server aliases of all virtual hosts in order to determine which virtual host block should be used. The order in which virtual hosts are checked is the order in which they appear in the configuration. As a fallback, if no virtual host matches, the virtual host that appears first in the configuration is selected.

Configuration problems related to virtual hosts can be hard to resolve. The following command is very helpful to "debug" a virtual host configuration - the command will simply print out which virtual hosts exist and in which order they appear:

apache2ctl -S

Besides ServerName and ServerAlias, a virtual host block usually contains at least the following information:

  • DocumentRoot directive: the system directory that corresponds to the root URL ("/") of the virtual host; for instance http://www.herzbube.ch/
  • ServerAdmin directive: defines the administrator person (often called the webmaster) to contact for that virtual host
  • CustomLog and ErrorLog directives: define the location of the corresponding log files


Wildcard Virtual Hosts

Imagine this setup:

<VirtualHost 1.2.3.4:80>
  ServerName foo.ch
  [...]
</VirtualHost>
<VirtualHost *:80>
  ServerName bar.ch
  [...]
</VirtualHost>

If a request comes in on 1.2.3.4 for server name bar.ch, Apache will match the first virtual host that uses an explicit IP address. For me this came as a surprise: I would have expected the wildcard virtual host to match because the first virtual host's server name does not match. It appears that the Apache developers have given the IP address a higher priority.

Note: In this example, the fallback mechanism seems to apply: If no virtual host matches, the virtual host that appears first in the configuration is selected.


How Virtual Hosts configurations are stored

On Debian, virtual host configuration files are stored in this folder:

/etc/apache2/sites-available

Configurations in that folder need to be enabled to take effect. Enabling a configuration happens by creating a symlink to the configuration file in the folder

/etc/apache2/sites-enabled

You can manually create such a symlink, but the recommended way to do this is by running the a2ensite utility. Example:

a2ensite www.herzbube.ch.conf


Important: As was stated further up, the "catch-all" default virtual host must appear as the first virtual host in the entire Apache configuration. Because vhost configuration files are loaded in ascending alphabetical order of their file name, the default vhost configuration file should be named with a prefix such as "000" - this ensures that it is loaded before any other vhost configuration files. Example:

root@pelargir:~# ls -l /etc/apache2/sites-enabled
total 0
lrwxrwxrwx 1 root root 35 Jun  8 18:30 000-default.conf -> ../sites-available/000-default.conf
lrwxrwxrwx 1 root root 47 Jul  5 21:38 aceexpander.herzbube.ch.conf -> ../sites-available/aceexpander.herzbube.ch.conf
lrwxrwxrwx 1 root root 40 Jul  3 16:13 bugs.herzbube.ch.conf -> ../sites-available/bugs.herzbube.ch.conf
lrwxrwxrwx 1 root root 39 Jun 15 15:05 cal.herzbube.ch.conf -> ../sites-available/cal.herzbube.ch.conf
[...]


Log rotation

Rotation of default log files such as /var/log/apache2/access.log is triggered by

/etc/logrotate.d/apache2

Rotation of non-default log files such as the ones created by the vhost configuratins below must be managed by a custom logrotate config snippet. Details are available on this page.


Concrete configuration: All my vhosts

The default Virtual Host

This is the content of sites-available/000-default.conf:

<VirtualHost *:80>
  ServerName unknown
  ServerAdmin webmaster@herzbube.ch
  ErrorLog ${APACHE_LOG_DIR}/unknown/error.log
  CustomLog ${APACHE_LOG_DIR}/unknown/access.log combined

  DocumentRoot /var/www/unknown
</VirtualHost>

# --------------------------------------------------------------------------------
# SSL Host
# --------------------------------------------------------------------------------
<VirtualHost *:443>
  ServerName unknown
  ServerAdmin webmaster@herzbube.ch
  ErrorLog ${APACHE_LOG_DIR}/unknown/error.log
  CustomLog ${APACHE_LOG_DIR}/unknown/access.log combined

  DocumentRoot /var/www/unknown

  Include conf-available/pelargir-herzbube.ch-vhosts-ssl.conf
</VirtualHost>


www.herzbube.ch

Pages that deal with stuff that is not covered in the vhost:


This is the content of sites-available/www.herzbube.ch.conf:

# --------------------------------------------------------------------------------
# www.herzbube.ch
# --------------------------------------------------------------------------------
<VirtualHost *:80>
  ServerName www.herzbube.ch
  ServerAlias 82.195.228.21 herzbube.ch
  ServerAdmin webmaster@herzbube.ch
  ErrorLog ${APACHE_LOG_DIR}/www.herzbube.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/www.herzbube.ch/access.log combined

  Redirect permanent "/" "https://www.herzbube.ch/"
</VirtualHost>

# --------------------------------------------------------------------------------
# SSL Host
# --------------------------------------------------------------------------------
<VirtualHost *:443>
  ServerName www.herzbube.ch
  # Explicitly name the public IP as an alias, otherwise access
  # to http://82.195.228.21 will be served by the "catch-all"
  # Virtual Host
  ServerAlias 82.195.228.21 herzbube.ch
  ServerAdmin webmaster@herzbube.ch
  ErrorLog ${APACHE_LOG_DIR}/www.herzbube.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/www.herzbube.ch/access.log combined

  DocumentRoot /var/www/www.herzbube.ch

  <Directory /var/www/www.herzbube.ch/>
    Include conf-available/pelargir-drupalsite.conf
  </Directory>

  # Slash at the end is important because the main software page
  # must remain accessible under "/software", i.e. no slash
  Alias /software/ /var/www/software/
  Alias /fuego/ /var/www/fuego/

  RewriteEngine on

  # ----------------------------------------
  # Host-based rules must appear before path-based rules, otherwise a
  # path-based rule might kick-in before the appropriate host-based rule
  # ----------------------------------------
  RewriteCond %{HTTP_HOST} ^herzbube.ch$
  RewriteRule ^.*$ https://www.herzbube.ch/ [R,L]

  # ----------------------------------------
  # Path-based rules must appear after host-based rules.
  # ----------------------------------------
  # Requests for documents of the old Eredain site will display the content of the
  # index page (which tells users that they have arrived at at a dead end). We try
  # to be clever and guess whether the user wants to see a German or an English page.
  # Note: We do not redirect ([R] flag) because with the current regexp this results
  # in an infinite redirect loop
  RewriteRule ^/Eredain/.*_d.* /Eredain/EredainIndex_d.shtml [L]
  RewriteRule ^/Eredain /Eredain/EredainIndex.shtml [L]
  # Requests for /svn or /websvn (or any path below those)
  RewriteRule ^/(web){0,1}svn /svn-replaced-by-git.html [L]

  Include conf-available/pelargir-herzbube.ch-vhosts-ssl.conf
</VirtualHost>

# --------------------------------------------------------------------------------
# new-site.herzbube.ch (for upgrading Drupal)
# --------------------------------------------------------------------------------
<VirtualHost *:443>
  ServerName new-site.herzbube.ch
  ServerAdmin webmaster@herzbube.ch
  ErrorLog ${APACHE_LOG_DIR}/www.herzbube.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/www.herzbube.ch/access.log combined

  DocumentRoot /var/www/drupal-new

  <Directory /var/www/drupal-new/>
    Include conf-available/pelargir-drupalsite.conf
  </Directory>

  Include conf-available/pelargir-herzbube.ch-vhosts-ssl.conf
</VirtualHost>

# --------------------------------------------------------------------------------
# old-site.herzbube.ch (for upgrading Drupal)
# --------------------------------------------------------------------------------
<VirtualHost *:443>
  ServerName old-site.herzbube.ch
  ServerAdmin webmaster@herzbube.ch
  ErrorLog ${APACHE_LOG_DIR}/www.herzbube.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/www.herzbube.ch/access.log combined

  DocumentRoot /var/www/drupal-old

  <Directory /var/www/drupal-old/>
    Include conf-available/pelargir-drupalsite.conf
  </Directory>

  Include conf-available/pelargir-herzbube.ch-vhosts-ssl.conf
</VirtualHost>


kino.herzbube.ch

This is the content of sites-available/kino.herzbube.ch.conf:

# --------------------------------------------------------------------------------
# kino.herzbube.ch
# --------------------------------------------------------------------------------
<VirtualHost *:80>
  ServerName kino.herzbube.ch
  ServerAdmin webmaster@herzbube.ch
  ErrorLog ${APACHE_LOG_DIR}/kino.herzbube.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/kino.herzbube.ch/access.log combined

  Redirect permanent "/" "https://kino.herzbube.ch/"
</VirtualHost>

# --------------------------------------------------------------------------------
# SSL Host
# --------------------------------------------------------------------------------
<VirtualHost *:443>
  ServerName kino.herzbube.ch
  ServerAdmin webmaster@herzbube.ch
  ErrorLog ${APACHE_LOG_DIR}/kino.herzbube.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/kino.herzbube.ch/access.log combined

  DocumentRoot /var/www/kino.herzbube.ch

  <Directory /var/www/kino.herzbube.ch/>
    Include conf-available/pelargir-drupalsite.conf
  </Directory>

  Include conf-available/pelargir-herzbube.ch-vhosts-ssl.conf
</VirtualHost>

# --------------------------------------------------------------------------------
# new-site.kino.herzbube.ch (for upgrading Drupal)
# --------------------------------------------------------------------------------
<VirtualHost *:443>
  ServerName new-site.kino.herzbube.ch
  ServerAdmin webmaster@herzbube.ch
  ErrorLog ${APACHE_LOG_DIR}/kino.herzbube.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/kino.herzbube.ch/access.log combined

  DocumentRoot /var/www/drupal-new

  <Directory /var/www/drupal-new/>
    Include conf-available/pelargir-drupalsite.conf
  </Directory>

  Include conf-available/pelargir-herzbube.ch-vhosts-ssl.conf
</VirtualHost>

# --------------------------------------------------------------------------------
# old-site.kino.herzbube.ch (for upgrading Drupal)
# --------------------------------------------------------------------------------
<VirtualHost *:443>
  ServerName old-site.kino.herzbube.ch
  ServerAdmin webmaster@herzbube.ch
  ErrorLog ${APACHE_LOG_DIR}/kino.herzbube.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/kino.herzbube.ch/access.log combined

  DocumentRoot /var/www/drupal-old

  <Directory /var/www/drupal-old/>
    Include conf-available/pelargir-drupalsite.conf
  </Directory>

  Include conf-available/pelargir-herzbube.ch-vhosts-ssl.conf
</VirtualHost>


acexpander.herzbube.ch

This is the content of sites-available/acexpander.herzbube.ch.conf:

# --------------------------------------------------------------------------------
# aceexpander.herzbube.ch
# --------------------------------------------------------------------------------
<VirtualHost *:80>
  ServerName acexpander.herzbube.ch
  ServerAdmin webmaster@herzbube.ch
  ErrorLog ${APACHE_LOG_DIR}/acexpander.herzbube.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/acexpander.herzbube.ch/access.log combined

  RewriteEngine on
  RewriteRule ^.*$ https://www.herzbube.ch/acexpander [R,L]
</VirtualHost>

# --------------------------------------------------------------------------------
# SSL Host
# --------------------------------------------------------------------------------
<VirtualHost *:443>
  ServerName acexpander.herzbube.ch
  ServerAdmin webmaster@herzbube.ch
  ErrorLog ${APACHE_LOG_DIR}/acexpander.herzbube.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/acexpander.herzbube.ch/access.log combined

  RewriteEngine on
  RewriteRule ^.*$ https://www.herzbube.ch/acexpander [R,L]
</VirtualHost>


bugs.herzbube.ch

See Bugzilla page.


cal.herzbube.ch

See DAViCal page.


gallery.herzbube.ch

See Gallery page.


git.herzbube.ch

See GitServer page.


ldap.herzbube.ch

See OpenLDAP page.


lists.herzbube.ch

See Mailman page.


mail.herzbube.ch

See Roundcube page (used to be SquirrelMail).


mysql.herzbube.ch

See MySQL page.


postgres.herzbube.ch

See PostgreSQL page.


wiki.herzbube.ch

See Mediawiki page.


pelargir.herzbube.ch

This is the content of sites-available/pelargir.herzbube.ch.conf:

# --------------------------------------------------------------------------------
# pelargir.herzbube.ch
# --------------------------------------------------------------------------------
<VirtualHost *:80>
  ServerName pelargir.herzbube.ch
  ServerAlias localhost
  Redirect permanent "/" "https://pelargir.herzbube.ch/"
</VirtualHost>

# --------------------------------------------------------------------------------
# SSL Hosts
# --------------------------------------------------------------------------------
<VirtualHost *:443>
  ServerName pelargir.herzbube.ch
  ServerAlias localhost
  ServerAdmin webmaster@herzbube.ch
  ErrorLog ${APACHE_LOG_DIR}/pelargir.herzbube.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/pelargir.herzbube.ch/access.log combined

  DocumentRoot /var/www/pelargir.herzbube.ch

  <Directory /var/www/pelargir.herzbube.ch/>
    Require all granted
    Options FollowSymLinks Includes
    php_admin_flag engine on
  </Directory>

  Include conf-available/pelargir-herzbube.ch-vhosts-ssl.conf
</VirtualHost>


www.grunzwanzling.ch

This is the content of sites-available/www.grunzwanzling.ch.conf:

# --------------------------------------------------------------------------------
# www.grunzwanzling.ch
# --------------------------------------------------------------------------------
<VirtualHost *:80>
  ServerName www.grunzwanzling.ch
  ServerAlias grunzwanzling.ch
  ServerAdmin webmaster@grunzwanzling.ch
  ErrorLog ${APACHE_LOG_DIR}/www.grunzwanzling.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/www.grunzwanzling.ch/access.log combined

  Redirect permanent "/" "https://www.grunzwanzling.ch/"
</VirtualHost>

# --------------------------------------------------------------------------------
# SSL Host
# --------------------------------------------------------------------------------
<VirtualHost *:443>
  ServerName www.grunzwanzling.ch
  ServerAlias grunzwanzling.ch
  ServerAdmin webmaster@grunzwanzling.ch
  ErrorLog ${APACHE_LOG_DIR}/www.grunzwanzling.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/www.grunzwanzling.ch/access.log combined

  DocumentRoot /var/www/www.grunzwanzling.ch

  <Directory /var/www/www.grunzwanzling.ch/>
    Include conf-available/pelargir-drupalsite.conf
  </Directory>

  RewriteEngine on
  RewriteCond %{HTTP_HOST} ^grunzwanzling.ch$
  RewriteRule ^.*$ https://www.grunzwanzling.ch/ [R,L]

  Include conf-available/pelargir-grunzwanzling.ch-vhosts-ssl.conf
</VirtualHost>

# --------------------------------------------------------------------------------
# new-site.grunzwanzling.ch (for upgrading Drupal)
# --------------------------------------------------------------------------------
<VirtualHost *:443>
  ServerName new-site.grunzwanzling.ch
  ServerAdmin webmaster@grunzwanzling.ch
  ErrorLog ${APACHE_LOG_DIR}/www.grunzwanzling.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/www.grunzwanzling.ch/access.log combined

  DocumentRoot /var/www/drupal-new

  <Directory /var/www/drupal-new/>
    Include conf-available/pelargir-drupalsite.conf
  </Directory>

  Include conf-available/pelargir-grunzwanzling.ch-vhosts-ssl.conf
</VirtualHost>

# --------------------------------------------------------------------------------
# old-site.grunzwanzling.ch (for upgrading Drupal)
# --------------------------------------------------------------------------------
<VirtualHost *:443>
  ServerName old-site.grunzwanzling.ch
  ServerAdmin webmaster@grunzwanzling.ch
  ErrorLog ${APACHE_LOG_DIR}/www.grunzwanzling.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/www.grunzwanzling.ch/access.log combined

  DocumentRoot /var/www/drupal-old

  <Directory /var/www/drupal-old/>
    Include conf-available/pelargir-drupalsite.conf
  </Directory>

  Include conf-available/pelargir-grunzwanzling.ch-vhosts-ssl.conf
</VirtualHost>


www.moser-naef.ch

This is the content of sites-available/www.moser-naef.ch.conf:

# --------------------------------------------------------------------------------
# www.moser-naef.ch
# --------------------------------------------------------------------------------
<VirtualHost *:80>
  ServerName www.moser-naef.ch
  ServerAlias moser-naef.ch
  ServerAdmin webmaster@herzbube.ch
  ErrorLog ${APACHE_LOG_DIR}/www.moser-naef.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/www.moser-naef.ch/access.log combined

  Redirect permanent "/" "https://www.moser-naef.ch/"
</VirtualHost>

# --------------------------------------------------------------------------------
# SSL Host
# --------------------------------------------------------------------------------
<VirtualHost *:443>
  ServerName www.moser-naef.ch
  ServerAlias moser-naef.ch
  ServerAdmin webmaster@herzbube.ch
  ErrorLog ${APACHE_LOG_DIR}/www.moser-naef.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/www.moser-naef.ch/access.log combined

  DocumentRoot /var/www/www.moser-naef.ch

  RewriteEngine on

  # ----------------------------------------
  # Host-based rules must appear before path-based rules, otherwise a
  # path-based rule might kick-in before the appropriate host-based rule
  # ----------------------------------------
  RewriteCond %{HTTP_HOST} ^moser-naef.ch$
  RewriteRule ^.*$ https://www.moser-naef.ch/ [R,L]

  # ----------------------------------------
  # Path-based rules must appear after host-based rules.
  # ----------------------------------------
  # All requests now display /index.html (which tells users that the site has
  # been shut down for privacy reasons). Note that we do not redirect ([R] flag).
  RewriteRule ^ /index.html [L]
  # Redirect requests to /gallery/hochzeit/ to the correct Gallery2 album
  RewriteRule ^/gallery/hochzeit/?$ https://gallery.herzbube.ch/v/2005-wedding/2005-bestofwedding/ [R,L]

  Include conf-available/pelargir-moser-naef.ch-vhosts-ssl.conf
</VirtualHost>


www.francescamoser.ch

This is the content of sites-available/www.moser-naef.ch.conf:

# --------------------------------------------------------------------------------
# www.francescamoser.ch
# --------------------------------------------------------------------------------
<VirtualHost *:80>
  ServerName www.francescamoser.ch
  ServerAlias francescamoser.ch
  ServerAdmin webmaster@herzbube.ch
  ErrorLog ${APACHE_LOG_DIR}/www.francescamoser.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/www.francescamoser.ch/access.log combined

  Redirect permanent "/" "https://www.francescamoser.ch/"
</VirtualHost>

# --------------------------------------------------------------------------------
# SSL Host
# --------------------------------------------------------------------------------
<VirtualHost *:443>
  ServerName www.francescamoser.ch
  ServerAlias francescamoser.ch
  ServerAdmin webmaster@herzbube.ch
  ErrorLog ${APACHE_LOG_DIR}/www.francescamoser.ch/error.log
  CustomLog ${APACHE_LOG_DIR}/www.francescamoser.ch/access.log combined

  DocumentRoot /var/www/www.francescamoser.ch

  RewriteEngine on

  # ----------------------------------------
  # Host-based rules must appear before path-based rules, otherwise a
  # path-based rule might kick-in before the appropriate host-based rule
  # ----------------------------------------
  RewriteCond %{HTTP_HOST} ^francemoser.ch$
  RewriteRule ^.*$ https://www.francescamoser.ch/ [R,L]

  # ----------------------------------------
  # Path-based rules must appear after host-based rules.
  # ----------------------------------------
  # Redirect (almost) all requests to moser-naef.ch
  RewriteRule !^/robots.txt$ https://www.moser-naef.ch [R,L]

  Include conf-available/pelargir-francescamoser.ch-vhosts-ssl.conf
</VirtualHost>


Test suite

What's this?

The links in the following sections should work. This can be used as a kind of test suite after changes to the Apache config have been made.

Note: The page DrupalMaintenance contains more tests that are related to Drupal.


Active resources

http://herzbube.ch/pgp-key-signing-policy.html 
Must display my PGP key signing policy.
git clone https://git.herzbube.ch/git/tools.git 
Must clone the Git repository "tools". If this works, it can be assumed that cloning will work as well for the remaining repositories.
https://git.herzbube.ch/gitweb.cgi 
Must allow to browse my Git repositories (not for cloning, web access only)
https://www.herzbube.ch/acexpander 
Must display (possibly after a redirect) the AceXpander project website. If this works, it can be assumed that the remaining project websites will work as well.
https://www.herzbube.ch/robots.txt 
The robots.txt file for the main site.
https://www.herzbube.ch/sitemap.txt 
The sitemap.txt file.
https://wiki.herzbube.ch/robots.txt 
The robots.txt file for the wiki site.
https://bugs.herzbube.ch/robots.txt 
The robots.txt file for the Bugzilla site.
https://www.francescamoser.ch/ 
Must redirect to moser-naef.ch
https://pelargir.herzbube.ch/ 
Must display a service overview.


Obsolete resources

https://www.herzbube.ch/PGPHerzbubeAtHerzbubeDotCH.txt 
Must display a notice that this file is no longer in use.
https://www.herzbube.ch/svn 
Must display a notice that I no longer use Subversion.
https://www.herzbube.ch/Eredain 
Must display a notice about the demise of Eredain.