Run two SSL sites on a single IP and port on Apache

The way Apache works, this cannot be done. Let me explain why:

Consider this request header for https://www.modphp.org/:

Code:
Host: www.modphp.org
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) Gecko/20050317 Firefox/1.0.2
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive

And the response header from Apache:
Code:
HTTP/1.x 200 OK
Date: Sun, 04 Sep 2005 04:12:35 GMT
Server: Apache/1.3.33 (Unix) mod_ssl/2.8.22 OpenSSL/0.9.7g
Set-Cookie: expires=Mon, 04 Sep 2006 04:12:35 GMT; path=/
Set-Cookie: phpbb2mysql_sid=80e31a987f9c08d2ca5ed666a6115287; path=/
Cache-Control: private, pre-check=0, post-check=0, max-age=0
Expires: 0
Pragma: no-cache
Content-Encoding: gzip
Vary: Accept-Encoding
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html

Now, let’s talk about how you would setup Apache if it really was possible to host two SSL sites on a single IP and PORT. The VirtualHost sections would look like something like this:
Code:

NameVirtualHost 68.178.150.145:443
<VirtualHost 68.178.150.145:443>
DocumentRoot “/www/modphp”
ServerName www.modphp.org
ErrorLog /etc/httpd/logs/modphp.error_log
TransferLog /etc/httpd/logs/modphp.access_log
SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
SSLCertificateFile /etc/httpd/conf/ssl.crt/modphp.org.crt
SSLCertificateChainFile /etc/httpd/conf/ssl.crt/sf_issuing.crt
SSLCertificateKeyFile /etc/httpd/conf/ssl.key/modphp.org.key
<Files ~ “\.(cgi|shtml|phtml|php3?)$”>
SSLOptions +StdEnvVars
</Files>
<Directory “/etc/httpd/cgi-bin”>
SSLOptions +StdEnvVars
</Directory>
SetEnvIf User-Agent “.*MSIE.*” \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0
CustomLog /etc/httpd/logs/modphp.ssl_request_log \
“%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \”%r\” %b”
</VirtualHost>

<VirtualHost 68.178.150.145:443>
DocumentRoot “/www/modphpSITE2″
ServerName www.modphpSITE2.org
ErrorLog /etc/httpd/logs/modphpSITE2.error_log
TransferLog /etc/httpd/logs/modphpSITE2.access_log
SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
SSLCertificateFile /etc/httpd/conf/ssl.crt/modphpSITE2.org.crt
SSLCertificateChainFile /etc/httpd/conf/ssl.crt/sf_issuing.crt
SSLCertificateKeyFile /etc/httpd/conf/ssl.key/modphpSITE2.org.key
<Files ~ “\.(cgi|shtml|phtml|php3?)$”>
SSLOptions +StdEnvVars
</Files>
<Directory “/etc/httpd/cgi-bin”>
SSLOptions +StdEnvVars
</Directory>
SetEnvIf User-Agent “.*MSIE.*” \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0
CustomLog /etc/httpd/logs/modphpSITE2.ssl_request_log \
“%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \”%r\” %b”
</VirtualHost>

Seems at first like it should work, but it doesn’t, let me explain why. Remember the request header and the response from above? When your browser connects to the web server on port 443, it must negotiate the SSL key exchange before it can accept the request header from the browser. At this level, it’s your browser talking to Apache over the IP address and port. In the SSL negotiation, Apache uses the SSLCertificateFile and SSLCertificateKeyFile, which are unique to the site.

So, this is the important part, Apache must pick the SSLCertificateFile and SSLCertificateKeyFile to use, and therefore the VirtualHost to use, BEFORE it has received the request header. After the SSL negotiation is complete, your browser then sends the request header, encrypted, to the server. At this point, the server reads the request header and gets the HOST name, but now it’s too late to switch to another VirtualHost… this would require another SSL negotiation, but your browser would never accept this.

Questions? Comments?

I would love it if someone proved me wrong. It is possible to create two SSL certs for two different sites wit the same SSL key. But, the certs would be different. Are they interchangeable? I don’t think so, but if they are, seems like Apache could translate the negotiation over to the other VirtualHost AFTER it gets the host from the request header…. but I don’t think so, just an imaginative thought. It’s still true, to the best of my knowledge, that it’s not possible to run two SSL sites on a single IP and port.

Comments are closed.