So you've configured Apache just like you want to. You've even added an SSL certificate to allow your users to securely navigate on your site. And you test it, and everything works, across all browsers you have access to... and you receive a dreaded call that there is a certificate error on a client (typically some higher up, because that is just your luck). What the hey?
Assuming you have tried all the simple debugging steps, ask to see the bug in person. And look for one thing only: is the certificate offered by the server the one you'd expect?
This was particularly annoying to find, so I'll explain a bit better. Go to your computer where everything works so nicely, and use a decent browser to go to the SSL version of it. Click on the locker icon and view the certificate. Head on to the advanced details.
For Chrome users, that would be by clicking on the green locker, going to the Connection tab, and clicking on "Certificate Information". Then go to the "Details" tab, and make a note of the "Serial Number" entry. Go through the same process on the client browser ( IE, which is likely what the person is using, will prompt to view the certificate when the problem arises ). Compare the serial.
If you have my kind of luck, the serials won't match.
Even worse, in my case this is a brand new Linux machine, with just one vhost - how is there a different certificate installed? The answer is hidden in the docs for mod_ssl FAQs :
Why can't I use SSL with name-based/non-IP-based virtual hosts?
The reason is very technical, and a somewhat "chicken and egg" problem. The SSL protocol layer stays below the HTTP protocol layer and encapsulates HTTP. When an SSL connection (HTTPS) is established Apache/mod_ssl has to negotiate the SSL protocol parameters with the client. For this, mod_ssl has to consult the configuration of the virtual server (for instance it has to look for the cipher suite, the server certificate, etc.). But in order to go to the correct virtual server Apache has to know the Host HTTP header field. To do this, the HTTP request header has to be read. This cannot be done before the SSL handshake is finished, but the information is needed in order to complete the SSL handshake phase. See the next question for how to circumvent this issue.
In plain English? Apache cannot tell which vhost this connection is for, until AFTER SSL is established. Thus, it uses the DEFAULT settings to establish the connection, and THEN processes your vhost-specific items.
Obviously, if you have one IP per vhost this shouldn't happen (that's why it is the recommended approach) - but as said before, I only have ONE vhost, and ONE ip, so ... ?
The answer is that when you install the mod_ssl apache module, it also creates its configuration file ( located at /etc/httpd/conf.d/ssl.conf on CentOS 6.4 ) which defines a DEFAULT configuration for ALL IPs, and since it's processed BEFORE your custom vhost config file, it takes precedence. Guess that I found in that config? Oh yes, a self-signed certificate.
But wait! How does it work just fine on your end, and others get stumped on this issue?
The answer here is how SSL is established. This is by no means a new problem - so a solution was created, by the name of Server Name Identification (SNI). This basically provides a hint to the server as to which vhosts it is interested in BEFORE establishing the SSL connection, thus allowing the server to pick the right certificate. Looking at the compatible browsers though, one line stands out like a sore thumb:
Internet Explorer 7.0 or later (on Vista, not XP)
There's your culprit. The solution? Just comment out the default vhost definition in ssl.conf. It really shouldn't be there in the first place, to be honest, but to be fair? If your vhost definitions are not like this
<VirtualHost *:443>
but like this instead:
<VirtualHost 1.2.3.4:443>
as they should, your wouldn't stump on this issue. You wouldn't learn about all this either though, so I'm going to count this whole event as "experience" to feel better about spending all that time on something so simple.