Nginx Rewrite Rules for WP Super Cache

July 16, 2008

(March 21, 2012) This is a post I made to the Slicehost forums a few years ago that some people have found useful. With the Slicehost brand ended and the forums currently shut down I’m reposting it here.

If you’re using a Wordpress blog, one thing you can do to significantly improve performance is install the WP Super Cache plugin. This will store static copies of pages accessed by users who don’t require any dynamic content generation (e.g., users who haven’t logged in or posted a comment) which should make up the vast majority of your visitors if your site is getting a lot of traffic. These static copies are set up on the filesystem so that they are easily accessible via some simple rewrite rules, so if the cache file already exists the web server can just serve it directly - the request never needs to be passed to PHP. This effectively turns your blog into a static site, allowing you to handle a lot of traffic using very few resources.

WP Super Cache goes a step further with an option to pre-gzip the content so the server doesn’t even need to do that. You probably have CPU to spare so the performance gain here is minimal, but the plugin provides it so I’ll show you how to use it.

Under Apache with mod_rewrite the rules to accomplish all this look something like so:

RewriteCond %{REQUEST_METHOD} !=POST
RewriteCond %{QUERY_STRING} !.*s=.*
RewriteCond %{QUERY_STRING} !.*attachment_id=.*
RewriteCond %{HTTP_COOKIE} !^.*(comment_author_|wordpress|wp-postpass_).*$
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{DOCUMENT_ROOT}/blog/wp-content/cache/supercache/%{HTTP_HOST}/blog/$1/index.html.gz -f
RewriteRule ^(.*) /blog/wp-content/cache/supercache/%{HTTP_HOST}/blog/$1/index.html.gz [L]

...
(similar rules for serving non-gzipped .html)

If you’re using nginx as your primary or front-end web server, translating these isn’t exactly straightforward, but it can be done with a little help from nginx’s support for variables:

location /blog/ {
  # enable search for precompressed files ending in .gz
  # nginx needs to be complied using –-with-http_gzip_static_module
  # for this to work, comment out if using nginx from aptitude
  gzip_static on;

  # if the requested file exists, return it immediately
  if (-f $request_filename) {
    break;
  }

  set $supercache_file '';
  set $supercache_uri $request_uri;

  if ($request_method = POST) {
    set $supercache_uri '';
  }

  # Using pretty permalinks, so bypass the cache for any query string
  if ($query_string) {
    set $supercache_uri '';
  }

  if ($http_cookie ~* "comment_author_|wordpress|wp-postpass_" ) {
    set $supercache_uri '';
  }

  # if we haven't bypassed the cache, specify our supercache file
  if ($supercache_uri ~ ^(.+)$) {
    set $supercache_file /blog/wp-content/cache/supercache/$http_host/$1index.html;
  }

  # only rewrite to the supercache file if it actually exists
  if (-f $document_root$supercache_file) {
    rewrite ^(.*)$ $supercache_file break;
  }

  # all other requests go to Wordpress
  if (!-e $request_filename) {
    rewrite . /blog/index.php last;
  }
}

Since nginx doesn’t support complex tests or nested if statements, the above is a little complex but will provide you with full Wordpress capabilities plus all the benefits of WP Super Cache. Any time that a cached static file exists and is appropriate to use, nginx will serve it directly and the request is never proxied to Apache or FastCGI.