But that’s impossible!!

HTTPS is just HTTP encapsulated inside an SSL tunnel. Apache’s virtual hosts are a clever “hack” whereby the Host header in the HTTP packet is verified. This alllows a single apache instance on a single IP/Port combination to serve a (not so) infinite number of differentes sites (aka vhosts).

Problem: The SSL tunnel is created before the first HTTP packet gets sent. Apache needs an SSL certificate but doesn’t have a Host header to match, hence cannot choose a virtual host.

Solution

This trick essentially does the matching of the Host header after the SSL connection has been established. How? Via some mod_rewrite magic!

Caveats

Although I said so, it’s not really that magical. There are a few things this trick does not solve.

  • The SSL certificate used will be common to all SSL vhosts.
  • Certain Apache directives may be common to all SSL vhosts (example: SuExecUserGroup). Basically anything you can’t override in a .htaccess file will be shared amongst vhosts.

The trick

The process is only 2 steps and involves modifying your Apache configuration. I assume you have a working SSL vhost configured.

  1. Create virtual hosts “map file”.
  2. Modify existing SSL vhost.

1. The virtual hosts map file

Create a new file in your Apache server root. Example:/etc/apache2/ssl.map

Write a list of virtual hosts and their respective DocumentRoot. Example:

foo.example.com        /var/www/foo.example.com/
bar.example.com        /var/www/bar.example.com/
# you can even put comments!
# Alias to bar
boar.example.com        /var/www/bar.example.com/

2. Edit your SSL vhost

Open your Apache config, inside the <VirtualHost> section of your SSL vhost, include the following code or include this file: Mass SSL vhosts Apache config.

Important: Make sure to edit line 8 to include the correct path to your ssl.map file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
### Mass SSL Vhosts ###
RewriteEngine on
 
#   define two maps: one for fixing the URL and one which defines
#   the available virtual hosts with their corresponding
#   DocumentRoot.
RewriteMap    lowercase    int:tolower
RewriteMap    vhost        txt:/etc/apache2/ssl.map
 
#   1. make sure we don't map for common locations
RewriteCond   %{REQUEST_URI}  !^/cgi-bin/.*
RewriteCond   %{REQUEST_URI}  !^/icons/.*
 
#   2. make sure we have a Host header
RewriteCond   %{HTTP_HOST}  !^$
 
#   3. lowercase the hostname
RewriteCond   ${lowercase:%{HTTP_HOST}|NONE}  ^(.+)$
#
#   4. lookup this hostname in vhost.map and
#      remember it only when it is a path
#      (and not "NONE" from above)
RewriteCond   ${vhost:%1}  ^(/.*)$
 
#   5. finally we can map the URL to its docroot location
#      and remember the virtual host for logging puposes
RewriteRule   ^/(.*)$   %1/$1  [E=VHOST:${lowercase:%{HTTP_HOST}}]

Restart Apache and you’re done. You should be able to browse (in https) the vhosts you added to your ssl.map file.

Grandma says: You don’t need to reload Apache when you edit your map file. Just create the document root folder on the filesystem, add a new entry to your map and you’re good to go.

Tags: , ,

15 Responses to “Hosting multiple SSL vhosts on a single IP/Port/Certificate with Apache2”

  1. #1 juju says:

    useful trick !

    crystal explanations, simple howto… perfect

    THANX !!

  2. #2 John DeStefano says:

    Sweet; thanks for the tip!

    Is there any way this can work when each vhost also has its own separate virtualhosts.conf file? Or does this work only when they all are configured the same way within the main configuration file (and the server map)?

  3. #3 Guido’s Notes » Blog Archive » Shared IP, multiple vhosts and multiple SSL certificates on Apache says:

    [...] Edit: Another way of achieving more or less the same is described here [...]

  4. #4 drax says:

    John DeStefano:
    As I said, with this technique, any directive in the main configuration file for the SSL vhost, is shared amonst all the SSL vhosts.
    However, you can have per-vhost directives if you move them from the configuration file (foo.conf) to a htaccess file in the corresponding DocumentRoot.

    Most, but not all directives can be overidden via a htaccess file.
    Hope this helped.

  5. #5 sean says:

    very cool. thanks.

  6. #6 links for 2008-09-09 says:

    [...] Hosting multiple SSL vhosts on a single IP/Port/Certificate with Apache2 [...]

  7. #7 Stephan says:

    Great trick, thanks!

    Is it possible to also use this for virtual hosts using ProxyPass?

    My config for a virtual host which I want to secure with ssl:

    ServerName tomcat.mydomain.com
    ProxyPreserveHost on
    ProxyPass / http://localhost:8080/
    ProxyPassReverse / http://tomcat.mydomain.com:8080/

  8. #8 Steve Tee says:

    Tidy solution. I have one question: if the document root specifiers are located on a box other than the Apache host, does this have an impact on the workability of this solution ?

  9. #9 drax says:

    @Stephan:
    As I stated in the Caveats: “anything you can’t override in a .htaccess file will be shared amongst vhosts.”
    Apparently, ProxyPass will not work in the context of a .htaccess file. Look at the “Context:” of a directive to check where it can be used. See: http://httpd.apache.org/docs/2.2/mod/mod_proxy.html#proxypass

    @Steve Tee:
    I don’t understand your question. If you are speaking about filesystems mounted from remote hosts, then no, it has no impact.

  10. #10 av says:

    Quick question: Will this solution address the cert mismatch errors that the browser will pop up since all of your subdomains are now using just one cert?

  11. #11 drax says:

    @av
    This depends on the subdomains you use, and the cert you use. If you have a look at my SSL cert (https://drax.sweon.net for instance) the “Common Name” is *.sweon.net
    This allows me to use the same SSL cert on all my sweon.net domains.

    So to answer your question, if your domains are different (ie: not subdomains of the same domain) then yes, you will get cert mismatch errors. If you use only one domain, and subdomains, you simply need a wildcard in the CN field of your cert.

  12. #12 Zechariah says:

    Impressa:) or as a Portuguese, vpechatlilso!

  13. #13 Jeff Sadowski says:

    I think I just came up with a clever solution. However web browsers will have to support srv records
    the problem with virtual hosts is that you can have only one ssl certificate per port (443)
    because ssl requires it encrypted before it sends any other information.
    A solution is to run a different key on different ports thus it could distinguish via port what key to encrypt with
    https://onedomain.com:443
    https://twodomain.com:444

    by default a web browser goes to port 443 for https
    Now if a web browser followed the rules of svr records you could tell the web browser to go to a different port using srv records

    _https._tcp.onedomain.com SRV 443
    _https._tcp.twodomain.com SRV 444

    then again if the web browser follows SRV records it should automatically go to the right port for ssl and you can have an ssl connection to a virtual host each host with its own certificate.

  14. #14 Jonas says:

    Hi, great trick. Whay about using it in an Apache+Tomcat environment? How can I map connections over mod_jk connector?

  15. #15 Ostario Mekambobo Grassnavor-Rothschild says:

    Look here for more stuff like this, original rasta:

    http://httpd.apache.org/docs/2.2/rewrite/rewrite_guide_advanced.html#mass-virtual-hosting

Leave a Reply