Setting up Pressflow and Varnish to work with HTTP and HTTPS
Posted by Chris Toler on 08 / 10 / 2010
Using Varnish and Pressflow together is an effective way to maximize the potential of your hosting environment, and can yield extraordinary results. The problem is, if you're not intimately familiar with Varnish, or the Linux command-line, getting it into a functional state can be something of a challenge. Out of the box, it is adequate at caching content for anonymous users, but that's where the fun ends.
On a recently completed (large) Pressflow project, we had to make some decisions about server architecture. Varnish, under heavy load, can suck down memory pretty quickly, so we opted to use two servers. The first one housed Varnish and Memcache, while the second one acted as the application environment (LAMP with APC).
After some basic tweaking, we got things running properly. Varnish was caching as expected, and things were moving along relatively well. We completed load-testing, and were very happy with the results. Then, a few weeks before launch, it was determined (by the client) that the site was to be for registered users only, and that there would be a recurring annual fee to remain a member, due to the premium content.
This obviously changed things a bit. If all users of the site needed to be authenticated, we'd have to modify the Varnish configuration to allow for this. There was a larger problem, though, that had not been anticipated. Varnish is an HTTP accelerator. Notice that I didn't say HTTP/HTTPS accelerator. It doesn't know what to do with HTTPS traffic, due to the encryption. We'd have to find a way to pass HTTPS traffic through Varnish in a meaningful way, or bypass it entirely. In addition to this, we needed to create a number of rewrite rules to redirect the user from HTTP > HTTPS on login, registration, and checkout pages (and vice versa).
Some quick Googling finds the Varnish FAQ, which recommends using an HTTPS accelerator like Pound or Stunnel. We decided to move forward with Pound, which was as easy as running the following commands from Server 1:
apt-get update
apt-get install pound… and then, the basic configuration to get Pound working:
## redirect all requests on port 443 ("ListenHTTPS) to Varnish (see "Service" below):
ListenHTTPS
Address 127.0.0.1
Port 443
Cert "/etc/ssl/certs/certificate.pem"
# allow PUT and DELETE also (by default only GET, POST and HEAD)
xHTTP 0
Service
BackEnd
Address 127.0.0.1
Port 80
End
End
End*note* If you have .key/.crt files (likely), you'll need to convert them into a single .pem key for Pound to work properly. This guide should help with that.
Running this configuration allowed both instances (HTTP/HTTPS) of the site to run independently of one another. For example, navigating to http://mysite.com/path would load the non-secure version of the site, and navigating to https://mysite.com/path would load the secure version.
Since Pound sends traffic to Varnish on port 80, Drupal's normal method of detecting HTTPS (Server variable - $_SERVER['HTTPS') doesn't work. Therefore, we can modify the value for a server variable that already exists, from the Pound configuration:
…
Cert "/etc/ssl/certs/certificate.pem"
HeadRemove "X-Forwarded-Proto"
AddHeader "X-Forwarded-Proto: https"
## allow PUT and DELETE also (by default only GET, POST and HEAD)?:
…… and add exclusion rules to Varnish to tell it to not cache the pages mentioned above:
sub vcl_fetch {
…
if (req.url ~ "user") {
return (pass);
}
if (req.url ~ "cart/checkout") {
return (pass);
}Now, you would think that since Varnish is passing the requests for these pages back to Apache in their entirety, we could just add some rewrite rules to .htaccess that use the X-Forwarded-Proto variable as a condition. In our testing, this didn't work, so we added a little logic to the Pressflow settings.php file to handle the work that conditionally enables the HTTPS variable in the presence of HTTP_X_FORWARDED_PROTO:
<?php
if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
$_SERVER['HTTPS']='on';
}else{
$_SERVER['HTTPS']='';
}
?>Now we can add rewrite rules to the Pressflow .htaccess file...
RewriteEngine On
#If NOT HTTPS, redirect /user/register, /user/login and /cart/checkout to HTTPS
RewriteCond %{HTTPS} !=on
RewriteCond %{REQUEST_URI} ^(/user/register|/user/login/|cart/checkout)$
RewriteRule ^(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [R,L]
#If HTTPS and not on /user/register, /user/login or /cart/checkout, redirect to HTTP
RewriteCond %{HTTPS} ^on$
RewriteCond %{REQUEST_URI} !^(/user/register|/user/login/|cart/checkout)$
RewriteRule ^(.*)$ http://%{SERVER_NAME}%{REQUEST_URI} [R,L]An alternative to modifying the settings.php and .htaccess files is to simply add some code at the top of your page.tpl.php file...
<?php
if (($_SERVER['REQUEST_URI'] == '/user/login' || $_SERVER['REQUEST_URI'] == '/user/register' || $_SERVER['REQUEST_URI'] == '/cart/checkout') && !$_SERVER['HTTP_X_FORWARDED_PROTO']){
header("Location: https://". $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']);
}elseif (($_SERVER['REQUEST_URI'] != '/user/login' && $_SERVER['REQUEST_URI'] != '/user/register' && $_SERVER['REQUEST_URI'] != '/cart/checkout') && $_SERVER['HTTP_X_FORWARDED_PROTO']) {
header("Location: http://". $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']);
}
?>and voila! Navigating to user/login, user/register or cart/checkout forces a rewrite of the URL to HTTPS, and navigating away from those pages returns the user to HTTP.



Add new comment
Comments
Very detailed write up. Thanks for the info!
Great post, but my sysadmin skills struggle to keep up. I was wondering how to configure the pound.cfg file if i'm using a certificate file that requires a key instead of a pem. Would I just add "Key" under the "Cert" reference?
I'm also getting this error: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName Do I need to adjust my host file or something? Or is there an issue with pound?
Thanks, Joe
This is something we initially tried as well, but there are two problems with this:
1. There are no clear ways to identify multiple files (.key, .crt, etc.).
2. Pound will not start correctly using anything but a .pem file.
Fortunately, you can convert your .key/.crt into a .pem. Try Googling something like "convert ssl to pem ubuntu", or try this link.
With regards to the second problem (Could not reliably determine the server's fully qualified domain name), this is generally an issue with the Apache2 default Virtual Host (/etc/apache2/sites-available/default). There are a couple of ways to clear it up, but the only reliable method I've found is to do the following:
1. Navigate to /etc/apache2/conf.d (user with sudo privileges).
2. Run cmd: sudo nano fqdn (or you can use "touch" then edit the file with nano/vi)
3. In this new empty file, add "ServerName localhost" (without the " marks).
4. Run cmd: sudo /etc/init.d/apache2 restart
This creates a manual override in Apache2, defining the fully qualified domain name as "localhost", and should prevent this error in the future.
Thanks for your input. I've updated the post with a note and a link out to explain how to convert the SSL certificate to .pem.
I have followed your instructions but am getting redirect loop error when going to /user/login.
My best guesses, unless you can provide any additional information:
1. Varnish isn't passing the requests back to Apache properly.
2. The rewrite rules in your .htaccess are not working properly.