How To Speed Up Wordpress With Nginx And WP Super Cache

We recently moved tech.nocr.at 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.
- Wordpress (of course)
- Apache2
- Nginx
- Wp-Super-Cache
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 8080Next 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/nginx.pid; 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 domain.com of course)
server { listen 80; server_name domain.com www.domain.com; error_log /var/log/nginx/domain.com.error.log; access_log /var/log/nginx/domain.com.access.log; # Main location (path to you blog, relative to your domain root) location / { proxy_pass http://127.0.0.1:8080/; 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) { 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 /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 . /index.php last; } }
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 tech.nocr.at front-end web servers.

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?
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.
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?
You should not forget to remove supercache rules from .htaccess when you move it to Nginx.
You are absolutely correct, you have to blow away your supercache rules from .htaccess or else the nginx rules won’t work properly.
Niiiiiicee
Saves my day, nginx power!
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’, ‘http://example.com:8080′);
define(’WP_HOME’, ‘http://example.com:8080′);
define( ‘WP_CONTENT_DIR’, $_SERVER['DOCUMENT_ROOT'] . ‘wp-content’ );
define( ‘WP_CONTENT_URL’, ‘http://example.com:8080/wp-content’);
Maybe thats the error?
Me again
As for the wp-config.php you don’ t need the :8080, example.com 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?
tech.nocr.at 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.
It’s very easy with a ip_hash directive, read:
http://wiki.nginx.org//NginxHttpUpstreamModule
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
Vito
nginx, without question.
@D.Z. – Yes. tech.nocr.at 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.
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:
http://pastebin.com/m56462fa4.
Am I doing something wrong?
Thanks in advance!
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
-tsl-
Never really played with MU to be honest.
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.
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 Wordpress.com now uses a similar setup.
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.
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!
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:
http://chrisjohnston.org/2009/setting-up-a-lemp-stack-ubuntu-904
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
Great examples. For additional performance tweaks, I'd also recommend using Xcache and the Xcache plugin for Wordpress (http://neosmart.net/dl.php?id=12). 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.