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
*note* If you have .key/.crt files (likely), you’ll need to convert them into a single .pem key for Pound to work properly.
Running this configuration allowed both instances (HTTP/HTTPS) of the site to run independently of one another. For example, navigating to a HTTP instance of a site would load the non-secure version of the site, and navigating to a HTTPS instance of a site 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:
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]
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.