Nov 23

If you host your own WordPress blog, it’s probably on Apache. That all fine and good. For most sites Apache works wonderfully, especially as it’s so easy to find information on it, on mod_rewrite and everything else that everyone else uses.

One of the alternatives is Nginx, a really fast webserver that streaks ahead of Apache in terms of performance, but isn’t quite as easy to use. That’s partly because Apache is the default webserver on most Linux distributions and hosts. Want to try Nginx? Here’s how.

Install Nginx. On Debian based systems that’s as easy as

aptitude install nginx

Nginx doesn’t talk PHP out of the box but one way to do it is via spawn-fcgi. Here’s where it gets complicated. (Docs summarised from here)

  1. Install php5-cgi. Again, on Debian systems, that’s
    aptitude install php5-cgi
  2. Edit /etc/nginx/sites-available/default and add the following chunk of code to the “server” section:
    location ~ \.php$ {
            include /etc/nginx/fastcgi_params;
            fastcgi_pass  127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param  SCRIPT_FILENAME  /var/www/nginx-default$fastcgi_script_name;
    }
  3. Install lighttpd for the spawning command.
    apt-get install lighttpd

    You’ll probably get an error at the end of the install if Apache is already running on port 80. Edit /etc/lighttpd/lighttpd.conf and uncomment the line

    server.port = 80

    and change 80 to 81. Now run the apt-get command again and it will install.

    /etc/init.d/lighttpd stop

    will stop lighttpd running. (You don’t need it)

  4. Create a new text file, /usr/bin/php-fastcgi with this:
    #!/bin/sh
    /usr/bin/spawn-fcgi -a 127.0.0.1 -p 9000 -u nobody -f /usr/bin/php5-cgi

    The user “nobody” should match the user Apache runs as to make things easier to transition.
    Make it executable with

    chmod 755 /usr/bin/php-fastcgi
  5. Create another new file /etc/init.d/init-fastcgi and make it executable with the chmod command too. Put this in the file:
    #!/bin/bash
    PHP_SCRIPT=/usr/bin/php-fastcgi
    RETVAL=0
    case "$1" in
        start)
          $PHP_SCRIPT
          RETVAL=$?
      ;;
        stop)
          killall -9 php
          RETVAL=$?
      ;;
        restart)
          killall -9 php
          $PHP_SCRIPT
          RETVAL=$?
      ;;
        *)
          echo "Usage: php-fastcgi {start|stop|restart}"
          exit 1
      ;;
    esac
    exit $RETVAL
  6. Start the PHP processes with
    /etc/init.d/init-fastcgi start

    and make sure it starts on every reboot with

    update-rc.d init-fastcgi defaults

That’s the PHP part of things. In Debian, the default root is “/var/www/nginx-default” so put an index.php in there to test things out. Stop Apache and start Nginx (if this is a test server only!) and visit your site. Works? Now to get WordPress and WP Super Cache working.

Open up /etc/nginx/sites-enabled/default in your editor and comment out the text already there with # characters. Paste the following in. Change paths and domains to suit your site. (via)

server {
        server_name  example.com www.example.com;
        listen   80;
        error_log   /www/logs/example.com-error.log;
        access_log  /www/logs/example.com-access.log;

        location ~ \.php$ {
                include /etc/nginx/fastcgi_params;
                fastcgi_pass  127.0.0.1:9000;
                fastcgi_index index.php;
                fastcgi_param  SCRIPT_FILENAME  /www/example.com/htdocs$fastcgi_script_name;
        }

        location / {
               gzip  on;
               gzip_http_version 1.0;

               gzip_vary on;

               gzip_comp_level 3;

               gzip_proxied any;

               gzip_types text/plain text/html text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

               gzip_buffers 16 8k;
               root   /www/example.com/htdocs;
               index  index.php index.html index.htm;
# 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;
               }
        }
}

I think the gzip settings above will compress cached files if necessary but Nginx can use the already gzipped Supercache files. The version of Debian I use doesn’t have gzip support compiled in, but if your system does, take a look at the gzip_static directive. Thanks sivel.

Finally, edit /etc/nginx/nginx.conf and make sure the user in the following line matches the user above:

user www-data;

I changed it to “nobody nogroup”.

Now, stop Apache and start Nginx:

/etc/init.d/apache stop; /etc/init.d/nginx start

WP Super Cache will complain about mod_rewrite missing, and you should disable mobile support.

How has it worked out? I only switched on Friday. The server did do more traffic than normal, but I put that down to the floods in Cork. Weekend traffic was perfectly normal.

Load on the site is slightly higher, probably because my anti-bot mod_rewrite rules aren’t working now. Pingdom stats for the site haven’t changed drastically and I think the Minify plugin stopped working, must debug that this week. Switching web servers is a huge task. I disabled mobile support in Supercache because I need to translate those rules to Nginx ones. A little birdie told me that he’s going to be writing a blog post on this very subject soon. Here’s hoping he’ll put fingers to keys soon.

Have you switched to Nginx? How has your switch worked out for you?

You might also like

If you like this post then please subscribe to my full RSS feed. You can also click here to subscribe by email. There are also my fabulous photos and funny videos to explore too!

30 Responses to “WordPress, Nginx and WP Super Cache”

  1. Thorsten says:

    btw i am using

            if (!-e $request_filename) {
                rewrite ^.+/?(/wp-.*) $1 last;
                rewrite ^.+/?(/.*\.php)$ $1 last;
                rewrite ^(.+)$ /index.php?q=$1 last;
            }
    

    i think you are running it all through index.php wondering if that would not cause trouble here and there?

  2. Donncha says:

    Tried that and the minified css still gives an error. “HTTP/1.0 400 Bad Request”

    One to look at later in the week.

  3. Andrea_R says:

    We’ve been meaning to do up a post with all the rewrite rules we use for nginx and our setup. Way cool!

  4. Stas says:

    I’m a big fan of nGinx and I’m using it already, but I wouldn’t recommend using it with php-fpm[1] instead of php-cgi. Also there’s a much simpler way of spawning a php-cgi socket by writing your own init-rc script[2] (requires no lighttpd).

    [1] https://bugs.edge.launchpad.net/ubuntu/+source/php5/+bug/397721
    [2] http://gist.github.com/241118

    Anyway, great article.
    Thanks.

  5. Amaury says:

    You can also install spawn-cgi without lighttpd ;) it’s now a different project ;)

  6. Rogi says:

    Hey Donncha,

    Yea, I have switched several of my WP sites so far, all work a treat on nginx+fastcgi — faster, lighter, and load tests show that they would not fall down under even extraordinarily high loads.

    I cut out the lightpd bit you used above though and did it an easier way (on Debian Lenny) with this script, have a dekko:

    http://thehook.eu/tools/nweb/

    Works well and does what it says on the tin etc. Recommended!

    R.

  7. Stas says:

    Oh, btw, here are some benchmarks I did when moving to nginx…
    http://stas.nerd.ro/index.php/read/518

  8. MG says:

    I’ve switched to nginx recently too and it’s sweet!
    I see major difference in memory usage and my server is much more relaxed now.

    In the past you did various tests with ab, could you please tell us how it works with your nginx setup?

    I run ab -c 100 -n 1000 on my server and
    without SuperCache I get 22 requests/sec
    with Supercache I get from 530 to 620 requests/sec
    and on static files (gif,txt etc) inside my WP blog I get from 3100 to 3600 requests/sec

  9. Ron says:

    I would only go back to apache if I had to :D

  10. DjZoNe says:

    I tried nginx with php-fpm, and spawn-cgi as well.

    I found php processing time has not changed much. So, now I’m using it as a proxy before apache, serving files and static pages generated by Super Cache.

    This way rewrites will work perfectly as before, and the load is lighter as well.

    The kind of switch back is because I’m willing to manage my client’s domains with an opensource CP (maybe SysCP, maybe ispCP omega), and I can’t tell them, to write nginx rewrite rules, etc. This way I am going to have the best of both worlds ;)

  11. James D Kirk says:

    Good stuff, Doncha. I actually set this up on a Slice over at Slicehost almost a year ago and have been loving it. The only different configuration I managed to work out was breaking out the code for Super Cache so I had the choice whether to implement it on a site install or not. Plus, I have different conf files for each domain install on the server (most of which are WPMU installs, BTW, ;) )

    So, in one of those site installs, I’ll create a new “location” section and insert the following:

    # Basic version of Wordpress parameters, supporting nice permalinks.
    # include /usr/local/nginx/conf/wordpress_params.regular;
    # Advanced version of Wordpress parameters supporting nice permalinks and WP Super Cache plugin
    include /usr/local/nginx/conf/wordpress_params.super_cache;
    }

    This allows me to choose regular or Super Cache processing!

    Thanks for posting this, cause it lets me and others know that we’re doing some things right!

  12. I have a bunch of virtual hosts on nginx, so I split out the WP/Super Cache rewrite rules into a separate file, and just include them from within whatever vhosts use WP. This makes things a lot cleaner.

  13. Showfom says:

    I use MobilePress plugins and want WP Super Cache support Mobile Service, how to config the nginx.conf?

  14. Michael says:

    Haha, man I can’t believe you just wrote this. I went through this whole process (with much googling) about a week ago. Talk about coincidences. This definitely has been a good move though, speed has increased incredibly from traditional setups of Apache and I’m much impressed.

    My move was prompted by work on a new Federated Micropublishing tool which for testing purposes is easier on Nginx+Fastcgi then it is with Apache.

    I’ll definitely keep this post bookmarked in case I have to do the process again in the future :P !

  15. Mark Jaquith says:

    I’m running nginx in front of Apache for the time being. nginx directly serves static files, but passes PHP on to Apache. I’ll likely start experimenting with nginx/PHP solutions soon. I also want to use the Memcached module to have nginx serve pages directly from there, for multi-server setups. Batcache, but faster, because it’s not loading up PHP at all.

  16. iamronen says:

    Though most of that was way over my head… I would be happy to learn more about your “anti-bot mod_rewrite rules” – is that to prevent automated “scanning” of the website (which I see happening alot in my stats)? does that create a problem for legitimate search engines?

    Thank you

  17. Relay says:

    Hello Donncha,

    After more than a year using Nginx+PHP in FastCGI mode and WP-Cache, the only thing I can say it’s that this is THE SOLUTION in order que get more of your servers. It’s solves 95% of the situations or problems on a website powered by PHP and MySQL.

    Some months ago, I tried the WP-SuperCache plugin, but it didn’t work and it doesn’t work nowadays actually. The problem is that the plugin doesn’t write to the disk. I have checked all the things I can: users on execution, rights on the folders, path’s and so on.

    Any though on this? Or any thing I can try?
    Regards

  18. Relay says:

    I’ll give a try…

    Thanks for the debug info.

  19. Relay says:

    Oh my god. My theme wasn’t closing the html code on the footer.php
    Found it with debug mode on in de WP-SuperCache:

    It was yelling “No closing html tag. Not caching.supercache No closing html tag. Not caching.”. I just added code on the footer of my theme and works like a thunder:

    Server Software: nginx/0.7.63
    Server Hostname: blog.contactosmallorca.es
    Server Port: 80

    Document Path: /
    Document Length: 30503 bytes

    Concurrency Level: 1
    Time taken for tests: 0.118 seconds
    Complete requests: 1000
    Failed requests: 0
    Write errors: 0
    Total transferred: 30739000 bytes
    HTML transferred: 30503000 bytes
    Requests per second: 8507.02 [#/sec] (mean)
    Time per request: 0.118 [ms] (mean)
    Time per request: 0.118 [ms] (mean, across all concurrent requests)
    Transfer rate: 255368.39 [Kbytes/sec] received

    Connection Times (ms)
    min mean[+/-sd] median max
    Connect: 0 0 0.0 0 0
    Processing: 0 0 0.0 0 0
    Waiting: 0 0 0.0 0 0
    Total: 0 0 0.0 0 0

  20. Relay says:

    Thanks for the tip, by the way :D

  21. [...] Holy Shmoly! » WordPress, Nginx and WP Super Cache (tags: wordpress nginx) [...]

  22. Shura Luberetsky says:

    As in my installation of Wordpress (on a Russian hosting server dobrohost.ru, where nginx is enabled by default), there appear some problems with wp-cron and nginx. In versions prior to 2.7, the tasks were spawned with this code:

    function spawn_cron() {

    //skipped some code

    if ( $argyle )
    fputs( $argyle,
    “GET {$parts['path']}?check=” . wp_hash(‘187425′) . ” HTTP/1.0\r\n”
    . “Host: {$_SERVER['HTTP_HOST']}\r\n\r\n”
    );

    }

    The socket was closed immediately, and nginx, working as a frontend for Apache, considered this as an error (#499 – the socket was closed prior to reply of webserver), the GET request was not relayed further and the task planner did not work. The problem can be resolved by adding a usleep(100000); delay (or a bit bigger, depending on the load of the server).

    In further versions, wp-cron was rewrited and the tasks were spawned by this code:

    wp_remote_post($cron_url, array(‘timeout’ => 0.01, ‘blocking’ => false));

    A 0.01 second delay is often too small, so this should be changed to

    wp_remote_post($cron_url, array(‘timeout’ => 0.1, ‘blocking’ => false));

    or something like this, for proper work of wp-cron and nginx.

  23. Donncha says:

    The Nginx experiment is over. It didn’t reduce the load on my server, it did quite the opposite in fact which might have been a result of using php5-cgi. The load went up after I changed to Nginx, and when I installed P2 it made things worse because of the p2ajax requests looking for updates. Supercache can’t cache those requests so PHP was being executed for each of those requests, and there were a lot of them. People leave their browsers open all day!

  24. Myatu says:

    Figures! I came up with my own solution just this week and you’ve already done it :)

    Anyway, what I had come up with isn’t too far from what you’ve already done, except that I use Apache for the back-end and I have added support for mobile phones.

    The mobile phone support was important to me, since I am using a different themes for those, which aren’t cached. I would post it here if it wasn’t such a long line (I’m checking for more mobile browsers than WP Super Cache does), so have a look at:

    http://www.myatus.co.uk/2009/12/17/faster-wp-super-cache-with-nginx/

    Just take that code that checks the user-agent, place it among the other “if .. set $supercache_uri” lines as shown here, and instead of ’set $mobile “M”;’ you use “set $supercache_uri ”;”. Don’t forget to disable it in WP Super Cache itself :)

  25. Kyrra says:

    I use nginx + apache (mod_php). I did some testing, comparing apache vs FastCGI, where both only handle PHP scripts. The processing speed and rate at which the data was returned was almost identical. FastCGI was just a sliver faster, but I accounted that to some .htaccess rules I still had in place that Apache had to deal with.

    Nginx on the front end is AMAZING when it comes to handing static content. Then let Apache or FastCGI do the heavy lifting of PHP and your website can handle much higher loads than normal.

  26. Ttech says:

    I recently switched, almost everything works except for Wordpress’s automatic upgrade core which sadly does not automatically do anything as of now.
    Otherwise ram load drastically cut back and makes the switch worth it.

  27. Allen says:

    This works for my blog, thanks for your post.

Leave a Reply

preload preload preload

Holy Shmoly! is Digg proof thanks to caching by WP Super Cache