Apache & DNS Wildcard Hosting with Mac OS X

Problem: You want to host all of your web development sites in ~/Sites, and you don’t want to fuck around with DNS and Apache configuration every time you start something new.

Solution: DNS wildcards and Apache Virtual Hosting!

The hard part: start by enabling BIND.

BIND is a domain name resolving system. It’s included in Mac OS X, but it isn’t turned on or configured by default. Odd.

$ sudo -s
$ rndc-confgen > /etc/rndc.conf
$ head -n 6 /etc/rndc.conf > /etc/rndc.key
$ emacs /var/named/dev.zone

/var/named/dev.zone should be:


; BIND data file for dev sites
;
$TTL    604800
@       IN      SOA     dev. root.dev. (
                     2008101920         ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
;
@       IN      NS      dev.
@       IN      A       127.0.0.1
*.dev.  14400   IN      A       127.0.0.1
$ emacs /etc/named.conf

Add this bit of content to /etc/named.conf after the line directory "/var/named";


forwarders {
    208.67.222.222;
    208.67.220.220;
};

Those are the OpenDNS servers. Substitute your own, add as many as your like, and use Google’s DNS if you want, but make sure you’re careful with the semicolons. I wasn’t, and it bit me in the ass.

While you’ve got named.conf open, add these lines above the zone "localhost" line:


zone "dev" IN {
     type master;
     file "dev.zone";
};

Again, be careful with the semicolons. BIND isn’t forgiving at all. If you mess it up, the syslog will contain lines like


2013-05-14 2:49:33.858 PM named[15397]: /private/etc/named.conf:39: missing ';' before '}'
2013-05-14 2:49:33.858 PM named[15397]: /private/etc/named.conf:53: missing ';' before 'zone'

Edit /System/Library/LaunchDaemons/org.isc.named.plist to enable the BIND daemon, by switching ‘true’ to ‘false’.

Launch the daemon:

sudo launchctl load /System/Library/LaunchDaemons/org.isc.named.plist

This should, in theory, be enough to get all the *.dev addresses pointed at the local machine, but it isn’t quite. Open up System Preferences.app and add 127.0.0.1 as a DNS entry for your internet connection. None of the documentation I saw mentioned this. It looks like the system won’t start actually using the local BIND system automatically.

The easy bit: configure Apache

Next up, configure Apache to respond to any request that matches *.dev/foo/bar.

Edit apache’s httpd.conf file and enable the virtual host include by uncommenting the include line:


# Virtual hosts
Include /private/etc/apache2/extra/httpd-vhosts.conf

And then edit the httpd-vhosts.conf file. Comment out all the stuff that’s there (it’s useful as an example) and add the following:


NameVirtualHost 127.0.0.1

    VirtualDocumentRoot /Path/to/Sites/%-2+

"%-2+" is magic: it means the part of the domain name before the last part. For foo.bar.dev it would be foo.bar.

Note that if you have Apache's mod_unique_id enabled you might be getting errors in Apache's log file that look something like this:


[Tue May 14 15:00:02 2013] [alert] (EAI 8)nodename nor servname provided, or not known: mod_unique_id: unable to find IPv4 address of "bernard"
Configuration Failed

Bernard is the local name of my computer. I added an entry in /etc/hosts for bernard, and everything worked out. Magic.


127.0.0.1    bernard

References: