Quick guide to wildcard Apache vhosts

[Edit: Added wildcard DNS section at the end, also link to dnsmasq setup instructions; also quick info on nginx defaults]

After my Debugging Drupal talk at Copenhagen today a lot of people wanted to know how to use a wildcard virtualhost, so I promised a quick writeup.

The purpose here is to be able to deploy new virtualhost-style Drupal sites on your localhost without doing any apache configuration (or /etc/hosts, if you deploy a nameserver).

Most of the time Drupal works better (and more like your production site) if you run it as a production site. If I have a site like debugging_example.com, I run it locally as debugging_example.l. That's way better than localhost/debugging_example, because all the URLs work right, and it's clean URLs.

The basic idea of what you have to do:

  • Enable mod_vhost_alias in Apache
  • Configure a catchall virtualhost
  • Tweak your .htaccess by uncommenting one line
  • Make an entry in your /etc/hosts

Here's the quick version of how to do it:

  1. Enable the mod_vhost_alias Apache module with sudo a2enmod vhost_alias (on Debian/Ubuntu, or however on your environment).
  2. /etc/init.d/apache2 restart
  3. Configure your httpd.conf or apache2.conf or whatever to provide a catchall. I use Debian/Ubuntu, and this is done with:
    • Create the file /etc/apache2/sites-available/catchall. Here is my catchall. You'll note you have to change the base path.
    • a2ensite catchall
    • /etc/init.d/apache2/reload
  4. Make an entry in your hosts file for the virtualhost you want to use.
  5. In your drupal .htaccess, uncomment the line that says
    # RewriteBase /
    so it says instead:
    RewriteBase /

And if you are ambitious and don't like adding each entry to /etc/hosts, here's a crude recipe for running bind9 (debian/ubuntu) to automatically resolve anything. I use *.l:

  • /etc/bind/named.conf.local, add
    zone "l" {
            type master;
            file "/etc/bind/db.l";
            notify no;
    };
  • Add the file /etc/bind/db.l, adjusted for your use:
    $ttl 38400
    @ IN SOA l.l. admin.l. (
    2009091700
    10800
    3600
    604800
    38400 )
    l. IN NS localhost.
    *.l. IN CNAME localhost
  • Change your computer to use localhost to resolve names. You can do this in the network config applet.

Update 2011-05-03: You might be interested in this article which shows how to do the same thing using dnsmasq, a lighter-weight DNS server.

Update 2011-05-03: To do the same thing in nginx as we did with Apache, this rule in /etc/nginx/sites-available/default will make nginx recognize any hostname like xxx.l or xxx.b or xxx.bigsony or www.xxx.b and send it to the /home/rfay/workspace/xxx directory.

  server_name   ~^(www.)?(?<domain>.+).(l|bigsony|b)$;
  root /home/rfay/workspace/$domain;

17 Comments

Could you clarify why we need

Could you clarify why we need to uncomment in .htaccess about RewriteBase, what's the reason behind this step?
Thank you very much,
Francesco

Doesn't work without it.

Hi Francesco - Unfortunately you just can't use the VirtualDocumentRoot without doing that. Clean URLs don't work.

I see, that's probably

I see, that's probably something that can be put at configuration level so you don't need everytime in .htaccess.
Is it possible to put the RewriteEngine On + RewriteBase / in the VirtualHost configuration?
Thanks for the quick reply!
Francesco

Great guide

This is a fantastic guide, Randy.

Francesco: have you managed to get the directives to work in the Virtual Host configuration? I've played with your suggestion and others but it appears maybe RewriteBase cannot be specified in the vhost?

RewriteBase: only valid in

RewriteBase: only valid in per-directory config files

So it's only valid in the .htaccess files.

you could also configure a wildcard DNS server

/etc/hosts doesn't support wildcard entries, but DNS entries do.

A bit more complicated, but you could enable a DNS server like BIND. Basically, set up DNS forwarding to your ISP's nameservers, or something like Google DNS or OpenDNS:

forwarders {
      8.8.8.8; // google
      8.8.4.4;
};

Create your own zonefile, setting in named.conf:

zone "mac" IN {
        type master;
        file "mac.zone";
        allow-update { none; };
};

And the zonefile:

$TTL  86400
$ORIGIN mac.
@            1D IN SOA   @ root (
                   42      ; serial (d. adams)
                    3H      ; refresh
                  15M     ; retry
                    1W      ; expiry
                   1D )        ; minimum

         1D IN NS    @
          1D IN A     10.37.129.2
*       IN      A       10.37.129.2

I used 10.37.129.2, that's my virtual adapter connecting with Parallels Desktop, so I can reach the domains from Windows virtual machines too. You can use 127.0.0.1 instead.

From then on, just create a directory, modify .htaccess, and it should work, any domain ending in .mac: a.mac, b.mac etc...

Added my config too

I added the config I use into the article. Thanks for posting this.

dnsmasq

I prefer dnsmasq, a really, really, REALLY simple DNS manager.

sudo apt-get dnsmasq
sudo nano /etc/dnsmasq.conf
# add
#   address=/l/127.0.0.1
# on end and save
sudo /etc/init.d/dnsmaq restart
sudo nano /etc/resolv.conf
# add (on top)
#   nameserver 127.0.0.1
# and save

that's all :)

a2enmod?

When you say...

Enable the mod_vhost_alias Apache module with sudo a2ensite mod_vhost_alias

Should this be a2enmod rather than a2ensite?

Fixed it

Sorry - fixed it. Of course it should be a2enmod. a2ensite is in my fingers.

Great Info

Great info on a tough topic, thanks Randy!

Ghost

I use http://github.com/bjeanes/ghost to add DNS entries as needed.

To install (assuming you have ruby and RubyGems installed):

gem install ghost

To add a new entry pointing to 127.0.0.1 just enter something like:

sudo ghost add mysite.local