112
projects completed
850,000
people use our software daily
45
successful integrations

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.

About the Author

Tech PM / Systems Administrator
Team member since 2006
Chris thinks of himself as a jack of all trades. From Systems Administrator to Designer, and most recently, Technical Project Manager, Chris has worn many hats at SingleMind. He is also the Director of Bridgecity Studio, which focuses exclusively on Drupal development.

Add new comment

Plain text

Type the characters you see in this picture. (verify using audio)
Type the characters you see in the picture above; if you can't read them, submit the form and a new image will be generated. Not case sensitive.

Comments

Submitted by Jon Garrison on

Very detailed write up. Thanks for the info!

Submitted by Joe on

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

Submitted by Chris Toler on

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.

Submitted by Chris Toler on

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.

Submitted by Anonymous on

I have followed your instructions but am getting redirect loop error when going to /user/login.

Submitted by Chris Toler on

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.

Our Clients Say

"For several years now, I have relied on SingleMind and their excellent engineers to manage the entire technology infrastructure for Partywirks.com!"
Larry McLean
Founder / CEO,
Partywirks
Want to make a lasting and meaningful impression with your users?
Drop us a line, we'll show you how to make it happen