How To Speed Up WordPress With Nginx And WP Super Cache


Edit: Fixes suggested in the comments have been applied to this howto

We recently moved over to a bunch of VPSs in order to achieve some decent load balancing and speed. Even though SliceHost isn’t a sponsor, I’d like to give them a quick plug. SliceHost is easily the greatest VPS provider bar none. You pick the Distro you want, you tweak it, you set it up, all ports are available, you do as you wish. If you setup multiple VPS with them you can even have private IP’s between them in order to load balance or cluster. Prices start as low as $20/month. If you use them, click this link so that I can get some referral love. Now on to the article..

WordPress can be a blessing and a pain at the same time. It’s great for displaying dynamic content yet it’s displaying that exact dynamic content that can put a massive stress load on your system. WordPress’s query routines aren’t exactly the best. Let’s not even go into how much of a hog Apache can be, but for running dynamic content and prettying up your url’s, mod_rewrite can’t be beat. By using WP Super Cache to help reduce the load on your SQL queries and by placing Nginx in front of Apache as a reverse proxy, you’d be amazed and how quickly your site will load and respond, even enough to withstand a digg of slashdot effect.

Here’s what you are going to need.

This how-to system assumes that you have your Webserver running under a debian style distro like Ubuntu, but It can easily be modified for other distros like Fedora or CentOS.

Assuming that you already have WordPress running under Apache the first thing we are going to do is install the Wp-Super-Cache plugin and active it under your plugin folder of your WordPress installation. Pay close attention to the installation steps in the included readme.txt file, you have to follow each step properly in order to ensure proper caching.

Wp-Super-Cache creates static html files of your pages lessening the load on your SQL queries. Once you have it running and working let’s move on to our next step, reconfiguring Apache.

Apache can be a resource hog, especially when it has to span multiple child processes in order to serve up content. We are going to have to do some tweaking to apache’s conf file so that it will work behind Nginx.

Open up your /etc/apache2/ports.conf file

sudo nano /etc/apache2/ports.conf

We need to change Apache from port 80 to 8080 as Nginx will be server content to the real world via port 80.

Listen 8080

Next we are going to turn KeepAlives off on Apache since Nginx will be dealing with all of the actual browser connections. This will ensure that all of the child processes in Apache die quickly and don’t hang around chewing up memory

Find the KeepAlive entry in your /etc/apache2/apache2.conf file and set it to off

KeepAlive Off

Now that we have made the needed changes in apache we need to restart it so that they take effect.

sudo /etc/init.d/apache2 restart

Next we are going to install Nginx which is a powerful and extremely lightweight http server that is perfect for reverse proxying or load balancing to backend servers like Apache.

sudo apt-get update | sudo apt-get install nginx

Once nginx is installed we are going to need to modify it’s config file located at /etc/nginx/nginx.conf

sudo nano /etc/nginx/nginx.conf

Your conf file should look like this.

user www-data;
worker_processes  2;
error_log  /var/log/nginx/error.log;
pid        /var/run/;
events {
	worker_connections  1024;
http {
	include       /etc/nginx/mime.types;
	default_type  application/octet-stream;
	access_log  /var/log/nginx/access.log;
	sendfile        on;
	tcp_nopush     on;
	keepalive_timeout  3;
	tcp_nodelay        on;
	gzip  on;
	include /etc/nginx/sites-enabled/*;

Now we need to move on to the actual site config file for our Nginx setup

sudo nano /etc/nginx/sites-available/default

Your file should look something like this (replace of course)

server {
        listen   80;
        error_log   /var/log/nginx/;
        access_log  /var/log/nginx/;
# Main location (path to you blog, relative to your domain root)
        location / {
            proxy_redirect     off;
            proxy_set_header   Host             $http_host;
            proxy_set_header   X-Real-IP        $remote_addr;
            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
            client_max_body_size       10m;
            client_body_buffer_size    128k;
            proxy_connect_timeout      90;
            proxy_send_timeout         90;
            proxy_read_timeout         90;
            proxy_buffer_size          4k;
            proxy_buffers              4 32k;
            proxy_busy_buffers_size    64k;
            proxy_temp_file_write_size 64k;
    # Static files location
        location ~* ^.+\.(htm|html|jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js)$ {
            root   /;
	# if the requested file exists, return it immediately
	if (-f $request_filename) {
	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 /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;

The above (especially the if statements) help Ngnix pass the static html files that are created by WP Super Cache directly to the users thus freeing up Apache to do nothing more than handle the dynamic requests.

Now it’s time to restart Ngnix and get the whole ball of wax working

/etc/init.d/nginx restart

What we have essentially done is taken the load of serving static files like images, css, and js files off of apache and passed it on to Ngnix which runs a lot faster and leaner than apache for serving static content. All requests for any dynamic content like php still gets passed on to apache. Clean and simple.

In closing let me mention that this is just what works for me, i’m sure that there might be a better way to use WP Super Cache and Ngnix to speed up WordPress, but what you see above has really helped with the load on both of the front-end web servers.

Frank's the editor in chief of He can be found surfing the internet and playing with gadgets. Follow him on twitter @franklinhares

  • Donncha O Caoimh

    Very nice tutorial! I can see that’d be useful for people with very busy sites. I might even give it a go!

    What about urls with GET parameters? I had to add rules in for the subscribe to comments unsub page, and also added a ?p= rule so those urls are passed to WordPress.
    Will your nginx rules serve the front page for those urls?

  • Frank Linhares

    Yes, all GET requests are transparently passed to the Apache server. Actually everything that doesn’t match one of the rules in the nginx virtual host file gets passed.

  • Glen Lumanau

    Thank you for the great articles. It’s works very nice.

    But I have 1 question

    How can we make nginx as load balancer with session keeping?

  • Bob

    You should not forget to remove supercache rules from .htaccess when you move it to Nginx.

  • Frank Linhares

    You are absolutely correct, you have to blow away your supercache rules from .htaccess or else the nginx rules won’t work properly.

  • Dirceu Jr.

    Saves my day, nginx power!

  • D.Z.

    Sorry, does this really work? I ve set this up on my WS, and i am redirected to localhost:8080, but it will stay there on 8080 and not on nginx:80. Trying to serve a css file or a gif results in a 301 and redirects me to WP`s document root index.php, but not to the css or gif file.

    What are you settings in the wp-config.php? Mine are:

    define(‘WP_SITEURL’, ‘‘);
    define(‘WP_HOME’, ‘‘);
    define( ‘WP_CONTENT_DIR’, $_SERVER['DOCUMENT_ROOT'] . ‘wp-content’ );
    define( ‘WP_CONTENT_URL’, ‘‘);

    Maybe thats the error?

  • D.Z.

    Me again :)

    As for the wp-config.php you don’ t need the :8080, is ok.

    As for gzip on: Some sources use gzip_static on. I ve tried both and use static.

    As for the wp-super-cache rewrite, the lines

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

    result in a permanent redirect to WordPress’ s main index, so i deleted the lines. It’s done at server level and tests file existence without root specified.

    @Frank: Have you ever tested the scenario written in your article by yourself?

    • Frank Linhares is currently running with nginx as a load balancer and proxy to two servers running apache. When I wrote the tutorial it was based on my setup.

  • Pingback: Linux tips | builder2

  • Richzendy

    Originally Posted By Glen LumanauThank you for the great articles. It’s works very nice.

    But I have 1 question

    How can we make nginx as load balancer with session keeping?

    It’s very easy with a ip_hash directive, read:

  • Vito Botta

    Hi, and many thanks for this article which I found searching Digg.
    I am customising a new WordPress blog which runs on Apache (I use Virtualmin for ease so I am stuck with Apache for now), and was wondering: between Nginx and Varnish, what would you recommend I install in front of Apache?
    Many thanks

    • Frank Linhares

      nginx, without question.

  • Frank

    @D.Z. – Yes. is running under nginx and wpsupercache. Been running like that for over 9 month like that. I have 2 frontend servers that are load balanced with nginx and have wpsupercache cache everything.

  • Vito Botta

    Hi again,
    I’ve got nginx + apache working now with wordpress, and am pretty sure the static files such as images, stylesheets and javascript files are being served by nginx rather than by apache.
    In fact, if I stop apache and request any of these static files directly, I can get them.
    But this does not happen with wp super cache pages.
    Super cache is working and I can see the comments it adds to the end of the html, but if I stop apache and try to see those pages (after making sure those pages are cached to disk once) with the browser (being not logged in wordpress), all I get is “502 bad gateway”. As soon as I start apache, these files are served fine again.
    How can I make sure that these files are served by nginx instead, when they exist on disk?
    Here is the nginx conf file for the blog I am working on:

    Am I doing something wrong?
    Thanks in advance!

  • tsl

    Hi, I can’t get nginx to work on wpmu. No matter what rules I try I always get a “502 Bad gateway” error when trying to login.

    Anyone with similar issues?

    My nginx conf
    My fastcgi.conf

    Thanks in advance

    • Frank Linhares

      Never really played with MU to be honest.

  • James D Kirk

    Getting your thoughts on Vito Botta’s questions about Nginx serving of the Super Cache files would serve many of us. I’m still doing the research on all of this prior to install and testing, so I’ve only the theory in my head, not the practical experience. I am thinking that there must be a log file somewhere that would help shed light to answer this definitively?

    Thanks for the great tutorial and am looking forward to your answers on the last couple of comments.

  • Vito Botta

    Hi James & all,

    in the end I have changed my setup by completely replacing Apache with Nginx, also for the serving of PHP pages.

    It is much, much faster and works great. I would suggest you do the same. By getting rid of Apache and using only Nginx instead, you will have a much faster web server using much less resources!

    I also recommend to patch PHP so that Nginx uses PHP-FPM rather than Lighty’s spanwn-fcgi which is often used but causes a number of issues.

    The only issue I am having with PHP-FPM is that after a while the application has been in idle, it automatically shuts down and have to restart it manually; so I need to figure out how to make this automatic.

    Apart from this – which I will be looking into as soon as I have some more free time – Nginx+PHP-FPM is definitely the fastest and the lightest (=best) combination I have seen so far.

    I believe that also now uses a similar setup.

  • Vito Botta

    I forgot to say, that once you replace Apache with Nginx, you have no more problems with WP Super Cache’s static files as long as you use a set of rules you can find on Slicehost.
    I can post some more details if anyone needs.

  • James D Kirk

    Good to hear from you, Vito. I’ve not heard about php-fpm, but have heard about nginx + php-fastcgi. I suspect many of these php processors are similar in that they provide nginx with the capability to process the dynamic aspects that Apache has been used for. Either way, got a pretty good handle on my game plan. Tomorrow, though. Very tired tonight!

  • James D Kirk

    Hey Vito, you probably have seen this page, however, it seems to have code to use for setting up spawn-fcgi to start up and initiate with nginx. Not sure if it will work or help you, but it might be worth your time:

  • Vito Botta

    Hi James, thanks.
    I have tried spawn-fcgi and as many others have given it up because it’s not really a stable method I think.
    PHP-FPM it’s much much better IMO.
    I only have to figure out that problem with the application going idle – am not really looking into it right now as am busy with other stuff

  • Pingback: HOWTO: Wordpress pretty URLs with Nginx 0.6 (the proper way) at bluebottle

  • DaVe

    Great examples. For additional performance tweaks, I'd also recommend using Xcache and the Xcache plugin for WordPress ( This plugin caches WordPress variables in memory.

    I'd also add the wp-optimize plugin to remove post revisions as well as optimize database tables.

  • Pingback: Back To Apache | Chesty's Blog

  • Pingback: nginx 参考资料 » LINKIDEA

  • Pingback: Wordpress deployment on the live server using chef (and vagrant)

  • Pingback: nginx giving of 404 when using set in an if-block

  • Pingback: nginx giving of 404 when using set in an if-block - Question Lounge

  • Zach Browne

    Unfortunately your

     addins are not working...
  • Zaman

    The images are not working anymore. I want to setup nginx as the frontend and your tutorial seems to be a detailed one. Please fix the images so that I can use it to setup Nginx as frontend for wordpress on my box.

    • Frank

      The plugin that was used when the article was written is no longer supported by WordPress.  I’ve fixed the problem.  Sorry about that.

  • Pingback: Optimiser son serveur Web avec Nginx | Développeur PHP Freelance