Apache SNI

2 minute read

I covered Server Name Indication (SNI) a while back, but it still surprises me how little people know about it. So, it’s time to look at configuring Apache to use SNI.

As a quick refresher, SNI allows multiple web sites to live on the same IP address, IPs being relatively expensive resources. SNI has been around for a long time (Apache has supported it since 2009), but was generally ignored do to the perception of a lack of browser support. However, it’s been hitting the big time with PAAS like Heroku and CDNs like CloudFront making it their default for HTTPS. The HTTP protocol includes support for sites sharing IPs by passing the hostname part of the URL in the Host HTTP header. The web server chooses what content to server by effective reassembling the URL from the Host header and the request path.1

This doesn’t work for HTTPS, the server send the SSL Certificate long before the browser sends any data about the request. SNI was added to the (TLS) negotiation so that the browser could request a specific certificate when making the initial connection, allowing the server to handle many certificates on a single IP.

SNI was added to Apache with version 2.2.12 (2009). The SNI supports come from the OpenSSL library, as long as Apache is built with OpenSSL v0.9.8f (2007) or later. If your Apache doesn’t support SNI, then you are running some really insecure servers and have much bigger problems than IP addresses.

The setup is simple. In each virtual host, enable SSL and define it’s SSL cert and key:

NameVirtualHost *:443

<VirtualHost *:443>
 ServerName www.example.com
 DocumentRoot /var/www/site
 SSLEngine on
 SSLCertificateFile /path/to/www.example.com.crt
 SSLCertificateKeyFile /path/to/www.example.com.key
 SSLCertificateChainFile /path/to/CA-Cert.crt
</VirtualHost>

<VirtualHost *:443>
 ServerName www.example.net
 DocumentRoot /var/www/example.net
 SSLEngine on
 SSLCertificateFile /path/to/www.example.net.crt
 SSLCertificateKeyFile /path/to/www.example.net.key
 SSLCertificateChainFile /path/to/CA-Cert.crt
</VirtualHost>

Boom, done. Apache will choose the virtual host’s cert based on SNI and everything will proceed smoothly.

Well, there is one setting. What happens when a browser without SNI support tries to connect?2 They will received the SSL cert from the default name-based virtual hosts. Depending on your feeling on old browsers support, you many want to set:

SSLStrictSNIVHostCheck on

This controls which, if any, virtual hosts a browser without SNI can access. Say the server has secure name-based sites A and B, with the default being A. When non-SNI browser connects to site B, it will be server site A’s cert and throw certificate miss-match error. SSLStrictSNIVHostCheck sets what happens if the user clicks though the error. With SSLStrictSNIVHostCheck set to off, the user can continue, with it global on they will not be allowed to access any site other than the default (who’s cert will match).

In addition, if you set SSLStrictSNIVHostCheck to on in the default virtual host SNI challenged browsers well be rejected by all sites, including the default. You can also set it on other virtual hosts to reject access to them specifically.

So, that’s it. Apache SNI is simple and supported by 99% of the browsers in the world. There’s simply no reason not to use it.

  1. Why not just send the whole URL in the request? Backwards compatibility that is now completely obsolete. 

  2. Which isn’t actually going to happen, were talking IE 6, IE on Windows XP, and supper old versions of Android. The Internet is already really broken for anyone using a browser that doesn’t support SNI. 

Comments