Nginx as a front-end proxy cache for WordPress

nginx-wp-love

The short version:

We put an nginx caching proxy server in front of our wordpress mu install and sped it up dramatically – in some cases a thousandfold. I’ve packaged up a plugin, along with installation instructions here – WordPress Nginx proxy cache integrator.

The long version:

Here at blogs.law.harvard.edu, our wordpress mu was having problems. We get a fair amount of traffic (650k+ visits/month), – combine that with ‘bots (good and bad) – and we were having serious problems. RSS feeds (we serve many from some pretty prominent blogs.) are expensive to create, files are gatewayed through PHP (on wpmu), and letting PHP dynamically create each page meant we were VERY close to maxing out our capacity – which we frequently did, bringing our blogs to a crawl.

WordPress – as lovely as it is – needs some kind of caching system in place once you start to see even moderate levels of traffic. There are many, many high quality and well-maintained options for caching – however, none of them really made me happy, or fit my definition of the “holy grail” of how a web app cache should work.

In my mind, caching should:

  • be high performance (digg and slashdot proof),
  • light-weight,
  • be structured to avoid invoking the heavy application frameworks it sits in front of. If you hit your app server (in this case, wordpress) – you’ve failed.
  • be as unobtrusive as possible: caching should be a completely separate layer that lives above your web apps,
  • have centralized and easily tweaked rules, and
  • be flexible enough to work for any type (or amount) of traffic.

So I decided to put a proxy in front of wordpress to static cache as much as possible. ALL non-authenticated traffic is served directly from the nginx file cache, taking some requests (such as RSS feed generation) from 6 pages/second to 7000+ pages/second. Oof. Nginx also handles logging and gzipping, leaving the heavier backend apaches to do what they do best: serve dynamic wordpress pages only when needed.

A frontend proxy also handles “lingering closes” – clients that fail to close a connection, or that take a long time to do so (say, for instance, because they’re on a slow connection). Taken to an extreme, lingering closes act as a “slow loris” attack, and without a frontend proxy your heavy apaches are left tied up. With a lightweight frontend proxy, you can handle more connections with less memory. Throw a cache in the mix and you can bypass the backend entirely, giving you absolutely SILLY scalability.

On nginx – it’s so efficient it’s scary. I’ve never seen it use more than 10 to 15 meg of RAM and a blip of CPU, even under our heaviest load. Our ganglia graphs don’t lie: we halved our memory requirements, doubled our outgoing network throughput and completely leveled out our load. We have had basically no problems since we set this up.

To make a long story short (too late), I packaged this up as a plugin along with detailed installation and configuration info. Check it out! Feedback appreciated: WordPress Nginx proxy cache integrator.

86 thoughts on “Nginx as a front-end proxy cache for WordPress

  1. Hey Dan thanks for taking the time to put this together.. I have set it up for our site and so far; its running great.

    I am trying to run *a different* wp blog on an ssl site located in a subfolder.. and having a little trouble getting the rules to work..

    my initial question is..

    does this setup cache *all* sites located under blogs.law.harvard.edu? like /djcp/ and the likes?

    or *only* blogs.law.harvard.edu?

    Thanks in advance.

  2. @brian – glad to hear it’s working. The default nginx config I put in the plugin docs caches everything under blogs.law.harvard.edu/, including all subfolders.

  3. I had trouble installing nginx as well. Is Could you point me to a good doc on this. I did look for one, but I had some trouble with the docs I have found. Thanks in advance.

  4. hi Dan,

    is it possible to host the Nginx front-end proxy on a separate server/IP other than on the actual WP installation itself?

  5. Hi Dan – Thanks for the plugin and write-up. Actually, your article convinced me to go with your sw-combo out of the 10 or so different architecture alternatives to my slow Apache/mod-PHP/Wordpress setup.

    However, I’ve been banging my head against the wall for a few days now trying to get it to work with WordPress installed in a subdirectory (ex: htdocs/wordpress/). I’m able to serve up stuff like htdocs/blah.html and even the WP Admin panel from the nginx front-end. But no dice on the actual WP content – I just get infinte 301s.

    Apache/WP seems to be working ok, because I can successfully hit http://127.0.0.1 at the right port (from my server of course) and get my WP site’s content.

    Strangely enough, I’ve only found one other solid reference on configuring WordPress in a subdir with nginx and they just ended up concluding that you shouldn’t do it!

    Any help would be greatly appreciated.

    _Khalid

  6. Hrm. Our blogs only appear to be on subdirectories because we’re using the multisite features of wordpress 3.x (formerly wordpress-mu). I’m guessing you’re seeing this problem because your front-end isn’t passing back the correct path to the backend. Are you using the mod_rpaf module on your apache backend, along with the proxy_set_header directives I’ve shown in the nginx configuration?

    The nginx frontend proxy – with the configuration I articulated in my plugin – cares little about the backend, so somehow you’re not passing the correct information back to your apache, or your apache install isn’t picking up the correct request headers.

  7. Thanks for the quick reply Dan.

    So, yes to using the proxy_set_header directives and mod_rpaf; I can see the correct client IP coming through in the Apache logs.

    I think this is an WordPress+Permalinks+.htaccess issue. I can access all the other HTML/PHP/image content that’s outside of WordPress a-ok. I can even hit stuff buried inside my wordpress/ subdir – ex: http://domain.com/wordpress/stuff/image.jpg. My guess is that this works b/c this dir is not affected by any .htaccess directives.

    As always, any other thoughts would be appreciated
    Thanks!

  8. is it possible to host the Nginx front-end proxy on a separate server/IP other than on the actual WP installation itself?

  9. Pingback: Nginx Reverse Proxy Cache for WordPress and Apache | WP Performance

  10. Should I use network activate option to activate this plug-in on a wordpress sub-folder multi-site set-up or separately enable it on all sub-folder sites?

  11. Dan,

    I think you might be able to save my bacon… I think your plugin will get me 90% of the way to where I need to be.

    Basically I have a WP3 network of sites where most of the blogs need to be on separate domains. There is a good plugin for handling this part (WP3 likes subdomains and subdirectories but multiple domains takes a kick in the teeth).

    But now I have an additional problem. Our main e-commerce site is a Rails App and we don’t really want to run our blog from that app; WP does a much better job and the marketing people aren’t our customer service folk or product managers. The Rails app is using an nginx front end and all requests to /blog are proxied to a WP2.x instance running on a separate EC2 instance (to which the firewall forbids access to all except our production server).

    Moving the main blog into the WP3 instance (which is now on RackSpace Sites) has proven difficult.

    Now it seems that your approach is not vastly different from what we currently have in our production environment. So I wonder if/how it could be adapted to handle separate domains (with either / or /blog or /articles).

    Any thoughts?

    Adam

  12. @kamal: You should enable it for every site you want to cache with nginx. If you’re going to cache every site, then I’d install it site wide.

  13. @Adam: I would look through the example configurations and read the nginx docs on how to proxy back. Nginx is flexible enough to front-end nearly any web app. You’d probably be setting up an additional upstream server (in the example configs, another stanza like “upstream wordpressapache{}”) and then just dispatch to that upstream when a certain URL pattern is requested. If you notice the “location” regex stanzas in the example configs, you’d just catch the URLs to your new wordpress and dispatch to the second upstream.

  14. Pingback: WordPress installed in htdocs subdirectory served by Apache sitting behind NginX?

  15. Thanks for this. I threw it up on one of my wordpress blogs and it works fantastically. I think I’ll eventually host it on a separate server than my blogs. Thanks again for the post.

  16. Pingback: Using a nginx reverse proxy for speed, security, and fun (1). | No Hair News

  17. hey dan –

    love the nginx proxy cache stuff, was using it for quite some time and am considering switching back. one question though – let’s say you have a bunch of servers that sit behind a hardware load balancer, and these servers are running nginx.

    if they are responsible for serving the same app, and requests are distributed round robin amongst the servers, it wouldn’t make much sense for each server to have its own local proxy cache in /var/lib/nginx/cache.

    was curious what you would suggest in that type of scenario? NFS?

  18. Had a bash at this and seems good so far. I wish all cache systems worked as easy.
    I can’t even begin to tell you about the issues I had with Google and translation cache pages.. in fact.. I don’t want to even think about it again :(

  19. In that case, I might refactor the nginx config to use memcached instead of a file cache. There’s also: https://github.com/FRiCKLE/ngx_slowfs_cache/ – which looks like a drop-in replacement for when you want to use a cache stored on nfs (untested, only read the README for five minutes).

    I have found – generally – that the cache doesn’t get all that huge and that the reaper does a pretty good job keeping it from growing too large – for this entire site (800+ blogs), our nginx cache right now is 225meg. You may find that the speed of a locally stored file cache is worth some duplicated space and a minor amount of duplicated CPU to populate it.

  20. Pingback: Scaling server cluster setup - Admins Goodies

  21. Pingback: In APC+PHP, how much RAM is too much? Is it okay to set apc.shm_size to many GB? - Admins Goodies

  22. Pingback: Nginx Reverse Proxy Cache for WordPress | Nginx Lighttpd Tutorial

  23. Hi Dan,

    Thanks for taking the time to help. I don’t know much about Linux and am wondering if there is some form of an auto installer, where i can type into Ubantu and presto I got everything set up but for minor tweaks? or at least get me to the point where only WP installation is left?

    Thanks in advance!
    Nick

  24. Should I use network activate option to activate this plug-in on a wordpress sub-folder multi-site set-up or separately enable it on all sub-folder sites?

  25. Hey Dan thanks for taking the time to put this together.. I have set it up for our site and so far it’s running great.

  26. Pingback: W3 Total Cache Alternatives | (not so) Tiny WordPress Stuff

  27. Pingback: WordPress nginx proxy cache - WordPress Select

  28. Hi Dan,

    Does the plugin version 0.1 work with WordPress 3.3.2?

    Your given Apache sample configuration is for 1 domain/vhost, would it be possible to have a configuration that would work with any new domain added to the server without having to manually add a vhost?

  29. Pingback: Scaling server cluster setup | PHP Developer Resource

  30. Pingback: Nginx Reverse Proxy Cache for WordPress and Apache

Comments are closed.