Hosting multiple SSL vhosts on a single IP/Port/Certificate with Apache2
January 24, 2008 on 2:07 pm | In Linux, admin, osx | 32 CommentsBut 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.
- Create virtual hosts “map file”.
- 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.
### 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.
32 Comments »
RSS feed for comments on this post. TrackBack URI
Leave a comment
Powered by WordPress with Pool theme design by Borja Fernandez.
Entries and comments feeds.
Valid XHTML and CSS. ^Top^
useful trick !
crystal explanations, simple howto… perfect
THANX !!
Comment by juju — 24 January 2008 #
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)?
Comment by John DeStefano — 6 February 2008 #
[...] Edit: Another way of achieving more or less the same is described here [...]
Pingback by Guido’s Notes » Blog Archive » Shared IP, multiple vhosts and multiple SSL certificates on Apache — 6 February 2008 #
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.
Comment by drax — 11 February 2008 #
very cool. thanks.
Comment by sean — 25 May 2008 #
[...] Hosting multiple SSL vhosts on a single IP/Port/Certificate with Apache2 [...]
Pingback by links for 2008-09-09 — 10 September 2008 #
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/
Comment by Stephan — 12 September 2008 #
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 ?
Comment by Steve Tee — 17 September 2008 #
@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.
Comment by drax — 20 October 2008 #
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?
Comment by av — 22 October 2008 #
@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.
Comment by drax — 22 October 2008 #
Impressa:) or as a Portuguese, vpechatlilso!
Comment by Zechariah — 6 November 2008 #
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.
Comment by Jeff Sadowski — 15 November 2008 #
Hi, great trick. Whay about using it in an Apache+Tomcat environment? How can I map connections over mod_jk connector?
Comment by Jonas — 2 December 2008 #
Look here for more stuff like this, original rasta:
http://httpd.apache.org/docs/2.2/rewrite/rewrite_guide_advanced.html#mass-virtual-hosting
Comment by Ostario Mekambobo Grassnavor-Rothschild — 5 December 2008 #
Пинайте своего хостера – сайт с трудом открылся
Comment by Online — 20 February 2009 #
Absolutely fantastic, it works exactly how I needed it. Thanks for sharing this, you are a genius!
Comment by Bill — 9 March 2009 #
Can this be done without the mapping file on a development box?
I love creating a directory for a website and being able to access the files with HTTP or HTTPS without having to touch Apache’s configuration files. I’ve been doing this with HTTP using Apache’s VirtualDocumentRoot directive, like this:
VirtualDocumentRoot /www/vhosts/%0/public
Thanks,
Ed
Comment by Ed — 21 March 2009 #
Interesting question, it can probably be done. I don’t have the time to test right now but I’ll look into it. Meanwhile, this might get you started:
RewriteRule ^/(.*)$ /var/www/%{HTTP_HOST}/htdocs/$1 [E=VHOST:${lowercase:%{HTTP_HOST}}]
Comment by drax — 21 March 2009 #
Thanks, Drax. That worked perfectly. Rewrite rules in .htaccess files (for Zend Framework) even continue to work.
Comment by Ed — 21 March 2009 #
really ? wow
Can you post (or mail me) your working set of rules so I can update this post ? I’m sure other people will enjoy it.
Comment by drax — 21 March 2009 #
Bummer – I ran into a problem with it on CakePHP stuff.
CakePHP has this in an .htaccess file in the main directory of the website:
RewriteEngine on
RewriteRule ^$ app/webroot/ [L]
RewriteRule (.*) app/webroot/$1 [L]
It normally redirects all requests to /app/webroot which has the following .htaccess file:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
Right now, page requests are failing and the browser ends up displaying the actual file path (rather than the URL). Kind of interesting. Any ideas?
Comment by Ed — 23 March 2009 #
Hey Drax, any idea why *.domain.com doesn’t take in the ssl.map file? I’m trying to specify one directory for SSL QA environment (qa.domain.com) and another directory for catch-all (*.domain.com).
The program enables infinite sub-domain registration over SSL, so I need to have this be just a bit more dynamic. Any tips?
Much appreciated!
Comment by Noel — 2 April 2009 #
Nice site! Thanks for the great post
Comment by advice hosting — 3 April 2009 #
Hi Drax, finally found my bookmark for this site… sorry it took a long time to get back…
The idea seemed great at first, but I’ve continued running into problems. Rewrites end up breaking for the other domains that I’m trying to hose. Also, the $_SERVER['DOCUMENT_ROOT'] variable ends always pointing to the first domain, even if you’re in other domains. Oh well, back to the drawing board =)
Comment by Ed — 6 April 2009 #
Ed –
Have a look here – http://book.cakephp.org/complete/27/Developing-with-CakePHP#Apache-and-mod_rewrite-and-htaccess-37
I quote –
For many hosting services (GoDaddy, 1and1), your web server is actually being served from a user directory that already uses mod_rewrite. If you are installing CakePHP into a user directory (http://example.com/~username/cakephp/), or any other URL structure that already utilizes mod_rewrite, you’ll need to add RewriteBase statements to the .htaccess files CakePHP uses (/.htaccess, /app/.htaccess, /app/webroot/.htaccess).
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
Comment by Adam — 9 June 2009 #
This was extremely helpful find via delicious.
It might be worth noting that in some cases people might get a:
(Error code: ssl_error_rx_record_too_long)
can be fixed with:
Comment by upbeat.linux — 1 July 2009 #
[...] blog ntic de revolunet » Hosting multiple SSL vhosts on a single IP/Port/Certificate with Apac… – [...]
Pingback by Bookmarks for 21.08.2009 through 26.08.2009 - mafflog — 28 August 2009 #
Hello. I have install this script on my test server. It is half working. I am testing on a my mail server and a website using ssl. The mail server is working fine. I am able to log in and view a few pages. However, my other dynamic site is not viewing pages that ends with https://domain.com/contact.html. I am able to view my homepage with this url https://domain.com. But if I have a anything after .com like (https://domain.com/index.html), it throws a 404 page error. Viewing in http is fine.
Comment by Duc — 7 September 2010 #
Duc:
wheck apache error log to see whats going on with this 404.
The last RewriteRule might be tweaked
Comment by Juju — 7 September 2010 #
Juju,
Thanks for the reply. I looked at my error.log and it say ‘File does not exist.’
I wouldn’t how to tweak the last RewriteRule.
Comment by Duc — 7 September 2010 #
I have tweaked my apache abit and now I have a 500 internal error for the second site.
I have a rewrite that redirects any inactive ssl domain to a non ssl site. To have apache read this .htaccess file, I had to set ‘AllowOverride all’ in my ssl vhost.
By doing that, my dynamic site now have a 500 internal error. The log says ‘Request exceeded the limit of 10 internal redirects due to probable configuration error. Use ‘LimitInternalRecursion’ to increase the limit if necessary. Use ‘LogLevel debug’ to get a backtrace.’
At this point, I am confusing the heck out of myself. Maybe I need to start over from the beginning of enabling ssl the correct way. I would appreciate any help on this.
Comment by Duc — 7 September 2010 #