VirtualHost takes over Directory, with no hostname in request

apache-2.2mac-osxvirtualhost

I have the following in apache.conf on my development workstation (actually included from /etc/apache2/users/andrew.conf):

<Directory "/Users/andrew/Sites/">
  Options Indexes MultiViews
  AllowOverride All
  Order allow,deny
  Allow from all
</Directory>

<VirtualHost *:80>
  DocumentRoot /Users/andrew/Sites/mysite/public
  ServerName mysite.dev
</VirtualHost>

I'm relying on the default settings in my new install of Mac OS X Lion, and just adding the VirtualHost directive.

When I get http://localhost/~andrew/ It doesn't try to serve the standard Directory index, rather it tries to serve the VirtualHost instead. I would expect this to happen only when accessing it by the ServerName "mysite.dev".

If I remove the VirtualHost, it serves the Directory index correctly.

Any ideas why this might be happening?

Update: I found that if I move the VirtualHost directive from users/andrew.conf to extra/http-vhosts.conf, then it works properly. I suppose that answers my question, but I'm still not clear on the root cause:

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

## extra/http-userdir.conf
UserDir Sites
Include /private/etc/apache2/users/*.conf
<IfModule bonjour_module>
   RegisterUserSite customized-users
</IfModule>

## extra/httpd-vhosts.conf
NameVirtualHost *:80

The configuration looks like they should still load the same thing, so I don't see why it would make a difference whether my VirtualHost is defined in extra/httpd-vhosts.conf or in my own users/andrew.conf

Update 2: In the extra/httpd-vhosts.conf file, I had commented out the "example" VirtualHosts that point to non-existent DocumentRoots. By putting these examples back in, my regular Directories now work as expected without falling through into the first catch-all VirtualHost.

So, the real question is: why does the apache configuration need an invalid VirtualHost for this to work?

Best Answer

Only requests that do not match any of the IP and port specified in declarations are handled by the main nonvirtual host.

When choosing the Virtual Host that will be used to serve a request, Apache works this way:

  1. It filters the Virtual hosts with matching IP adress and port.
  2. IF no match was found, the default nonvirtual host is used.
  3. IF one match was found, it is used.
  4. IF more than one match was found, the first of them that also matches in ServerName or ServerAlias is used.
  5. IF more than one match was found, but subsequently no match in ServerName or ServerAlias could be established, the first Virtual host matching in IP address and port is used.

Therefore, if you specify a Listen 80 and <VirtualHost *:80> directives in your config, that means the nonvirtual host cannot possibly be used, because everything we listen for will be caught by that Virtual host and even if the ServerName on that virtual host mismatches, it will still be matched to it according to my rule 5.

Solution is to change your main nonvirtual host into a virtual one. Putting <VirtualHost *:80> declaration around the DocumentRoot directive for the main host should be enough.

You can read about this more in Apache documentation on Name-based Virtual Host Support. Check especially the Main host goes away note.