News, technical help and self-indulgent geek speak

A Mass Virtual Hosting Environment for Web Development

Last modified: Jul 11th 2007, 05:58 | | | Comments(2)

“Virtual hosting” can be a real boon for configuration maintenance when it comes to website hosting. There are a plethora of different ways to configure the Apache webserver to present different content based on the hostname used to refer to the server.

Unfortunately for the web developer, most of the “howto” guides are aimed at commercial hosting. The needs there are different, generally the purpose of the exercise is to place the document roots of each customer’s hosted sites into their home directory rather than having them all stored in a central location. The end user never has to manually create a new virtual host (not without paying for the privilege anyway).

Here at Nextstudio though, each of our developers needs to have a working copy of potentially dozens of recent projects. It needs to be easy for a developer to quickly have a running copy of an existing project within such a working directory, without needing a system administrator dedicated to configuring a new document root each time, not to mention cleaning up dozens and dozens of old sites – branches of projects that never made it to the final product, quick working copies that were checked out only for reference and used for a grand total of fifteen minutes, or older projects that are no longer being actively worked on.

We’ve now used two separate configurations for virtual hosting for developers. One is especially easy to set up, the other is a little more flexible.

mod_vhost_alias

mod_vhost_alias is aimed directly at virtual host aliases, and thus seemed ideal for the job at hand under our initial situation. We successfully used this setup for over a year, before moving to the second solution below when upgrading our system.

Basically, to use mod_vhost_alias, your apache VirtualHost section will contain a VirtualDocumentRoot rather than DocumentRoot. In our configuration developer site URLs looked like: http://siteid.developername.dev.nextstudio.net/. This would be mapped do a document root located in ~developername/dev_html/siteid located on the development webserver.

The configuration required to achieve this was:

<VirtualHost *>
	ServerAdmin serveradmin@example.com
	ServerName      dev.nextstudio.net
	ServerAlias     *.dev.nextstudio.net *.dev
	VirtualDocumentRoot /home/%2/dev_html/%1
	<Directory /home/>
			Options Indexes FollowSymLinks MultiViews
			AllowOverride All
			Order allow,deny
			allow from all
	</Directory>
	LimitInternalRecursion 15
	ErrorLog /var/log/apache2/dev_error.log
	LogLevel debug
	CustomLog /var/log/apache2/dev_access.log combined
	ServerSignature On
</VirtualHost>

Note that in the VirtualDocumentRoot, %1 and %2 refer to the period-separated components of the hostname used to access the webserver. Coupled with a wildcard DNS entry that pointed *.dev.nextstudio.net towards the webserver, all that was required was for the user to create ~/dev_html/anothersiteid, drop some content into that directory and http://anothersiteid.developername.dev.nextstudio.net/ was accessible.

mod_rewrite

mod_rewrite offers a rule-based rewriting engine to rewrite the URL of a request on the fly. Much more general and flexible than mod_vhost_alias it is our current preferred solution for virtual hosting. To configure virtual hosting with mod_rewrite, or new VirtualHost section looks like:

<VirtualHost *>
	ServerAdmin system_elvis@nextstudio.net
	ServerName      dev.nextstudio.com.au
	ServerAlias     *.dev *.dev.nextstudio.com.au
	DocumentRoot /var/www/
	LimitInternalRecursion 15
	<Directory /home/>
			Options Indexes FollowSymLinks MultiViews
			AllowOverride All
			Order allow,deny
			Allow from all
	</Directory>
	RewriteEngine on
	RewriteMap lowercase int:tolower
	RewriteMap vhost txt:/etc/apache2/dev_dirs.map
	RewriteCond ${lowercase:%{SERVER_NAME}} ^([^.]+)\.([^.]+)\.dev\.
	RewriteCond %1.${vhost:%2} ^([a-z]+)\.(/.*)$
	RewriteRule ^/(.*)$ %2/%1/$1 [L]
	ErrorLog /var/log/apache2/dev_error.log
	LogLevel debug
	LogFormat "%{Host}i %h %l %u %t \"%r\" %s %b" vcommon
	CustomLog /var/log/apache2/dev_access.log vcommon
	ServerSignature On
</VirtualHost>

Note that we use the RewriteMap directive to map usernames to directories, which is a little more flexible than the older solution. It allows us to control which users on the machine can have files in their home directory accessed in this manner, in addition to letting us specify the location of document roots per-user, which was required after reshuffling the location of home directories based on the user’s group. We have to create the map referred to, /etc/apache2/dev_dirs.map:

user1   /home/staff/user1/dev_html
user2   /home/contractors/user2/dev_html

Now http://site1.user1.dev.nextstudio.com.au refers to /home/staff/user1/dev_html/site1, while http://site2.user2.dev.nextstudio.com.au refers to /home/contractors/user2/site2. If the user does not exist within the map, the request will be directed to the default document root in /var/www.

A note about per-directory mod_rewrite with either solution

We make quite heavy use within projects of the per-directory usage of mod_rewrite, within .htaccess files. In many simple cases users of mod_rewrite can ignore the local directory prefix. For example our favourite framework, Cake, ships with a .htaccess file that looks like:

<IfModule mod_rewrite.c>
	RewriteEngine On
	RewriteCond %{REQUEST_FILENAME} !-d
	RewriteCond %{REQUEST_FILENAME} !-f
	RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
</IfModule>

Sadly if the directory prefix has been rewritten with either of the above methods, this will fail. mod_rewrite offers the RewriteBase directive to specify the local directory prefix in this situation, and the manual states:

If your webserver’s URLs are not directly related to physical file paths, you will need to use RewriteBase in every .htaccess file where you want to use RewriteRule directives.

In our case, the modified .htaccess is:

<IfModule mod_rewrite.c>
	RewriteEngine On
	RewriteBase /
	RewriteCond %{REQUEST_FILENAME} !-d
	RewriteCond %{REQUEST_FILENAME} !-f
	RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
</IfModule>

Add a comment

New commenters only

New commenters only

If you've left a comment with us before, just skip down to the "All commenters" section below.

All commenters

All commenters

We hate spam! We won't display your email address (even mangled) on the site, nor will we show it to any third party. We will send a verification email the first time you submit a comment, and any other time someone tries to submit a comment using your email address without providing your password. If we send you anything else, it will be personally written and solely in response to your comment.

Leave the password blank if this is your first time posting or you do not have easy access to your password. If the password is blank you will receive an email containing both your password and a confirmation link.

If this is your first time posting, be sure to fill our your name and (optional) website address above.

To prevent automated (spam) comments, we require that you copy the letters and/or numbers from the image above into this input box.