I found out today that I have been configuring Apache wrong for a while. How long?

Around 15 years.

ಠ_ಠEveryone who has configured Apache has probably done work with the <VirtualHost> declaration. You know how it usually looks:

NameVirtualHost *:80 <VirtualHost myserver.whatever.com:80>

# apache stuff goes here

</VirtualHost>

But that’s wrong. It’ll work, of course, but it’s wrong.

Continuing, if you have multiple sites, and you were devoted to doing it wrong (or at least, never bothering to really grok the documentation), you’d do this:

NameVirtualHost myhost.mydomain.com:80

NameVirtualHost myotherhost.mydomain.com:80

# and maybe we’ll get fancy with some https

NameVirtualHost securehost.mydomain.com:443 <VirtualHost myhost.mydomain.com:80>

ServerName myhost.mydomain.com

# Other Stuff

</VirtualHost> <VirtualHost myotherhost.mydomain.com:80>

ServerName myotherhost.mydomain.com

# Other Stuff

</VirtualHost> <VirtualHost securehost.mydomain.com:443>

# ServerName securehost.mydomain.com

SSLEngine On

# Other SSL stuff here

# Other domain stuff here

</VirtualHost>

Yeah, that’s wrong too. When you do it, you get these niggling little errors:

[warn] NameVirtualHost myotherhost.mydomain.com:80 has no VirtualHosts

[warn] NameVirtualHost securehost.mydomain.com:443 has no VirtualHosts

…but it works. So you’re like, “that’s weird. well, it works, I’ll figure it out later”, but you never do.

Well today, I did. And I learned something. And I’m going to tell you.

See, my misunderstanding was all in the “NameVirtualHost” line. I thought, because it’s called “NameVirtualHost”, it wanted the name of a virtual host. Call me crazy.

Instead, what that ACTUALLY does is point to an IP address that Apache is going to listen on. It’s just that by putting the domain name, it looks it up, and comes up with the IP address.

So if myhost.mydomain.com has a DNS record (or a host file entry) of 192.168.1.10, then these two lines are equivalent:

NameVirtualHost myhost.mydomain.com:80

NameVirtualHost 192.168.1.10:80

…and that’s why it always works. Up above, when you wanted to enable ssl, you were like, “I’ve already go a web server; let’s make this happen!”, so you just added a CNAME (or maybe another A record) for securehost that matched the IP of myhost, and went on your merry way.

The other symptom of this problem, apart from the “has no VirtualHosts” error, is whenever you have multiple SSL-enabled sites on the same IP address. The way https works is that the browser connects to port 443, magic happens when the certificates are exchanged, and then your web browser is like, “it would be a shame to waste this secure tunnel. How about you give me securehost.mydomain.com?”.

If you have “othersecurehost.mydomain.com”, too, then you might see the problem. The certificate has already been exchanged by the time the server figures out what site the client wants…and the way it works in the apache config is that the first virtualhost encountered take precedence, so your browser might WANT othersecurehost, but the certificate it gets will be for securehost.

This sort of things will be obvious if you administer multiple externally-visited https-enabled sites, because you’ll get certificate warnings all over the place. But 99% of what I work with has been building https-enabled internal sites where I just never, as a rule, bothered to spin up a good reliable certificate service and sign my own stuff, so I was always USED to certificate errors.

The site that finally helped me figure out what was going on is Apache Common Misconfigurations. And incidentally, there has been an extension to the SSL standard that allows multiple SSL-enabled sites to be hosted on the same IP address. Not every browser supports it (for those of you who still cater to IE6 users, you would now have even more sympathy from me, if that were possible), but if you have a reasonable expectation that your visitors’ web browsers were written after the Bush administration, then you should be OK.

So anyway, today I’m happy to admit my blinding ignorance if you will accept for it an offering of knowledge. I apologize for going this long without investigating that one weird little error that didn’t seem to be causing problems. I’ll try not to let it happen again. Too often.