Setting wildcard virtual hosts on Mac OSX

Like many others wanting to start developing web based apps, I was faced with the challenge of setting up my local Apache server to support wildcard subdomains. On my live server, in Plesk, this is an easy task and only requires changing the CNAME record in DNS. The process is not as simple for Apache but can still be done. That is to say, when you need *.yourproject.local to point to localhost.

While working on a booking application, I needed to do a lot of testing on my local machine. Since each user in this web app has their own subdomain, I needed to map *.bookingapp.local in my web browser to the localhost. What seems pretty straightforward, turned out to be complex. /etc/hosts does not support wildcard subdomains names.

So what’s the answer? Set up your own local nameserver. It’s easier than you might think. OSX 10.6 and greater ships with ISC Bind (the standard for serving DNS) and it’s pretty easy to setup. Here’s how I did it.

Credit goes to Mike Ferrier, whom I originally learnt this technique from. I’ve made some changes and hope to use this as my own resource for when I forget the address of his blog.

  1. Edit /etc/named.conf and add the following block, called a “zone”, to the end of the file:

    zone "local" IN {
    type master;
    file "bookingapp.zone";
    allow-update { none; };
    };
    
  2. Add the .local zone file

    The line file “bookingapp.zone” in named.conf sets the name of the file where bind will look for information on that zone. Those zone files live in /var/named so next we add the file /var/named/bookingapp.zone

    Here’s what mine looks like:

    $TTL  60
    $ORIGIN local.
    @      1D IN SOA  localhost. root.localhost. (
              45    ; serial (d. adams)
              3H    ; refresh
              15M    ; retry
              1W    ; expiry
              1D )    ; minimum
    
          1D IN NS  localhost.
          1D IN A  127.0.0.1
    
    *.local. 60 IN A 127.0.0.1
    bookingapp.local. 60 IN A 127.0.0.1
    *.bookingapp.local. 60 IN A 127.0.0.1
    

    You’ll want to replace bookingapp with the domain of your choice, and local with the suffix you want.

  3. Check your config and zone files

    Bind comes with some handy utilities for checking your config and zone files.

    $ sudo named-checkconf /etc/bookingapp.conf
    $ sudo named-checkzone local /var/named/bookingapp.zone
    zone local/IN: loaded serial 45
    OK
    

    The first command will have no output if your config is set up properly. The second should say “OK”.

  4. Start bind

    An easy way to start up bind is to add it to the Launch Daemons of the root user:

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

    After running this command, launchd should have started up named automatically:

    $ ps aux | grep named
    root 2467 0.0 0.2 2441740 8380 ?? Ss 6:43pm 0:05.48 /usr/sbin/named -f
    

    As an added bonus, this command makes sure named is automatically started on boot, too.

  5. Verify things are working

    dig is the swiss army knife of DNS tools, and is distributed with Bind, so it’s already around on OSX. Asking localhost to resolve a few domains will verify things are working:

    $ dig @localhost bookingapp.local
    
    ; <<>> DiG 9.7.3 <<>> @localhost bookingapp.local
    ; (2 servers found)
    ;; global options: +cmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 39642
    ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0
    
    ;; QUESTION SECTION:
    ;bookingapp.local.		IN	A
    
    ;; AUTHORITY SECTION:
    .			10800	IN	SOA	a.root-servers.net. nstld.verisign-grs.com. 2011100801 1800 900 604800 86400
    
    ;; Query time: 50 msec
    ;; SERVER: 127.0.0.1#53(127.0.0.1)
    ;; WHEN: Sat Oct  8 21:07:17 2011
    ;; MSG SIZE  rcvd: 111
    

    $ dig @localhost bookingapp.local
    
    ; <<>> DiG 9.7.3 <<>> @localhost bookingapp.com
    ; (2 servers found)
    ;; global options: +cmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 27427
    ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1
    
    ;; QUESTION SECTION:
    ;bookingapp.com.		IN	A
    
    ;; ANSWER SECTION:
    bookingapp.com.	86400	IN	A	127.0.0.1
    
    ;; AUTHORITY SECTION:
    bookingapp.com.	86400	IN	NS	localhost.
    
    ;; ADDITIONAL SECTION:
    localhost.		86400	IN	A	127.0.0.1
    
    ;; Query time: 0 msec
    ;; SERVER: 127.0.0.1#53(127.0.0.1)
    ;; WHEN: Sat Oct  8 21:08:40 2011
    ;; MSG SIZE  rcvd: 89
    

    $ dig @localhost foo.bookingapp.local
    
    ; <<>> DiG 9.7.3 <<>> @localhost foo.bookonlinehq.local
    ; (2 servers found)
    ;; global options: +cmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 27424
    ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0
    
    ;; QUESTION SECTION:
    ;foo.bookonlinehq.local.		IN	A
    
    ;; AUTHORITY SECTION:
    .			10800	IN	SOA	a.root-servers.net. nstld.verisign-grs.com. 2011100801 1800 900 604800 86400
    
    ;; Query time: 46 msec
    ;; SERVER: 127.0.0.1#53(127.0.0.1)
    ;; WHEN: Sat Oct  8 21:10:03 2011
    ;; MSG SIZE  rcvd: 115
    

    The important part of each of those three queries is the IN A response in the ANSWER SECTION. In each, we see that the domains resolve to 127.0.0.1, which is exactly what we want.

  6. Add 127.0.0.1 to your list of DNS servers

    Open System Preferences and open the Network pane. On the left side, select the network adapter you use (most likely your wireless adapter), and then click Advanced. Under the DNS tab, click the + button under the DNS Servers list, and add 127.0.0.1. Click OK, then click Apply.

    That's it, you're done!

  7. Reloading Bind and flushing DNS

    If you ever change anything in your config or zone files, you can signal Bind to reload its config using:

    sudo rndc -p 54 reload

    It's also a good practice to increment the serial field in your zone file any time anything changes. This lets any DNS client know to invalidate its DNS cache for that zone. So in our example, our serial is set to 45. So if you made any change, you'd change it to 46 before saving the file and reloading Bind.

    Some people also reported that they needed to flush their local DNS cache after doing this (I didn't.) If you feel you need to, you can by issuing the command:

    dscacheutil -flushcache

Posted

in

, , , ,

by

Tags:

Comments

2 responses to “Setting wildcard virtual hosts on Mac OSX”

  1. Neil Avatar
    Neil

    Great post!
    I’m using lion. When I disconnect wifi I can no longer work on my local sites. Any tips?
    Cheers

    1. steedancrowe Avatar

      Neil, You can try restarting apache,

      sudo apachectl restart

      Also, if you’re using virtual hosts, make sure your local ip is added to your DNS in system preferences -> network -> advanced -> DNS I have to add my localhost(127.0.0.1) here for virtual servers to work. Just make sure you remove it when you want access to that same web address through the net. If you’re using different naming for your virtual servers then you can leave it there all of the time.

      Hope that helps.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.