“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>

Any solution for not modifying the .htaccess?
It's kinda a hassle when your .htaccess is in version control. But I haven't found anyway to fix the RewriteBase problem globally in my vhost :(
Have you learned anything new in this regard?
Thanks,
Jacob
P.S. My stop gap is a custom name for my .htaccess
I'm afraid not, it seems as though RewriteBase needs to be set up within each .htaccess.
http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html#rewritebase says:
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.
As much as I've tried to disprove the statement, it seems to stand up to scrutiny.
Your solution is an interesting one. What we've normally done when it comes up is to move .htaccess out of source control and instead generate it with a suitable RewriteBase if needed as part of the build process, but YMMV if you don't have freedom to make that sort of change.