Setting up a wildcard Apache virtual host + a wildcard DNS

When starting work on a new web project, one of the steps involved is setting up a virtual host on the computer you use for development. Doing this manually becomes boring very quickly: creating a new virtual host file, enabling it, adding an entry to your hosts file, and, depending on your setup, symlinking a few things together to make it all work.

Instead of doing this every time, you can set up a 'catchall' virtual host, also called a 'wild card' virtual host. This virtual host makes it possible to use one top level domain on your local, and automatically have its subdomains map to a given documentroot on your local, following a certain pattern. I'm using .dev as a top level domain in my setup.

Additionally, a local DNS server (I'm using Dnsmasq) can send all requests for a certain domain to your localhost, so you don't have to worry about adding entries to your hosts file ever again.

Below are the steps to accomplish this. Note that I'm on Ubuntu 13.04, but this should work equally well (possibly/probably with some slight changes here and there) on other Ubuntu versions or Linux flavors, and on OSX.

(This post is based on the very helpful write ups/comments by Randy Fay, Giles Smith, Daniel Hahler and Grumot. Since I had to make slight changes to make it work for me, I decided to do a quick write up, for future reference.)

1. Set up catchall vhost

  1. Enable Apache's vhost_alias module: sudo a2enmod vhost_alias
  2. Create /etc/apache2/sites-available/catchall.conf
    <VirtualHost *:80>
      ServerAlias localhost *.dev #wildcard catch all
      VirtualDocumentRoot /path/to/your/workspace/%1/public
      UseCanonicalName Off
      <Directory "path/to/your/workspace">
        Options FollowSymLinks
        AllowOverride All
        Order allow,deny
        Allow from all
        Require all granted
      </Directory>
    </VirtualHost>
  3. Enable the virtual host configuration: sudo a2ensite /etc/apache2/sites-available/catchall.conf
  4. Reload the Apache configuration: sudo service apache2 reload
  5. To make the VirtualDocumentRoot work, you also need to uncomment the following line in Drupal's .htaccess: # RewriteBase /

2. Configure local wildcard DNS server

  1. Install Dnsmasq: sudo apt-get install dnsmasq
  2. Since Ubuntu's NetworkManager uses dnsmasq, and since that messes things up a little for us, open up /etc/NetworkManager/NetworkManager.conf and comment out the line that reads dns=dnsmasq. Restart NetworkManager afterwards: sudo restart network-manager.
  3. Make sure Dnsmasq listens to local DNS queries by editing /etc/dnsmasq.conf, and adding the line listen-address=127.0.0.1.
  4. Create a new file in /etc/dnsmasq.d (eg. /etc/dnsmasq.d/dev, and add the line address=/dev/127.0.0.1 to have dnsmasq resolve requests for *.dev domains. Restart Dnsmasq: sudo /etc/init.d/dnsmasq restart.

3. Have your localhost resolve domain names

The final step needed to make this all work, is to let your localhost resolve domain names you request, so it can send requests for the .dev domain (or whatever domain you choose to work with), to your localhost. To do this, you open the file /etc/dhcp/dhclient.conf, and uncomment the line #prepend domain-name-servers 127.0.0.1;. Next, refresh your local DNS handling by typing sudo dhclient.

4. Enjoy doing no more manual virtual host configuration

To add a new site on your local machine, you can simply add your codebase in your workspace, e.g. /path/to/your/workspace/drupal/public, will be accessible on your local at the url drupal.dev.

Comments

Ben replied on

Thanks! This guide helped me out a lot. I think you forgot to mention one step, right after you create /etc/apache2/sites-available/catchall. Before reloading Apache, run:

sudo a2ensite /etc/apache2/sites-available/catchall

Otherwise, requests to example.dev will be routed to /path/to/your/workspace.

Also, here's a tip for people, like me, who needed to have wildcard subdomains as well. This /etc/apache2/sites-available/catchall works for me, although I don't know whether it's the most efficient way of doing this:

<VirtualHost *:80>
ServerAlias localhost *.*.dev
VirtualDocumentRoot /var/www/%2/public
UseCanonicalName Off
<Directory "/var/www">
Options FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
</Directory>
</VirtualHost>

<VirtualHost *:80>
ServerAlias localhost *.dev
VirtualDocumentRoot /var/www/%1/public
UseCanonicalName Off
<Directory "/var/www">
Options FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
</Directory>
</VirtualHost>

brunodbo replied on

Thanks for catching that! I added the missing step.

Good to know that wildcards subdomains work as well.

TNanek replied on

I used your instructions to make a system where if I go to *.d6 it'll bring me to Drupal-6 root, *.d7 the Drupal-7 root and *.d8 the Drupal-8 root. Note: D6 is there more for migration purposes than actual development.

In the /etc/apache2/sites-available folder I have three files (they have to end in .conf now, but the names are only needed in the following command).

$ cat /etc/apache2/sites-available/6.conf
<VirtualHost *:80>
ServerAlias *.d6
VirtualDocumentRoot /path/to/your/drupal-6/install
UseCanonicalName Off
<Directory "path/to/your/drupal-6/install">
Options FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
</Directory>
</VirtualHost>

$ cat /etc/apache2/sites-available/7.conf
<VirtualHost *:80>
ServerAlias *.d7
VirtualDocumentRoot /path/to/your/drupal-7/install
UseCanonicalName Off
<Directory "path/to/your/drupal-7/install">
Options FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
</Directory>
</VirtualHost>

$ cat /etc/apache2/sites-available/8.conf
<VirtualHost *:80>
ServerAlias *.d8
VirtualDocumentRoot /path/to/your/drupal-8/install
UseCanonicalName Off
<Directory "path/to/your/drupal-8/install">
Options FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
</Directory>
</VirtualHost>

$ sudo a2ensite 6 7 8

This way, one can make the most use of Drupal's multi-site abilities and reuse the code. The critical thing to keep in mind here is that the folders in the sites directory contain the settings for the individual sites, and I prefer to keep modules,themes and libraries separated too when possible (when a module assumes the location of the library is 'sites/all/library' it won't work, but when employing the libraries module it would). These would all be folders within the sites' specific folder, in addition to the default of files, which needs special permissions.

I usually install via drush using the site-install command, be sure to use the --sites-subdir option if going this way.

Simen Schikulski replied on

Thank you so much! This was absolutely perfect for my local web development server! :)

Josh replied on

Thanks for this great tutorial!

I noticed I got an error trying to run instruction 1.3. -- when using a2ensite, you don't need to specify the entire path to the catchall file, so the command should look like this:

sudo a2ensite catchall

Simen Schikulski replied on

This works great from my laptop and from outside our local network, but accessing the catch all virtual host from a VM running on the same server that's running the apache virtual host does not work.

Anyone know why? Error.log doesn't say anything. My guess is the dnsmasq server - though I'm not sure how this is or should be configured.

Cezar replied on

Can you please help me with a modification? I have followed the tutorial above and it works beautifully - thank you!!!
However, I would like the server to serve the website from a directory named exactly like the domain. What do I need to modify in order to get the local domain to point to /var/www/testsite.dev/ instead of /var/www/testsite/ ?

Thank you !!!

Add new comment