Lightning Fast WordPress with Nginx + Redis

in WordPress


I had the good opportunity of ending up on Jim Westgren’s article about using Redis as a front end cache and it didn’t take long for me to try it on one of my virtual boxes. The results were unbelievable, pages that were taking about 0.2 to 1.2 seconds were now loading at 0.0025 second on average.

Unfortunately, Jim is a bit busy showing us how he configured his server to run on nginx + php-fpm + apc + cloudflare + redis. So in doing my share to make (a part of) the web faster and better for everyone here’s an install guide to get you started.

What You Need

A virtual private server (VPS), this unfortunately is required. If you are in a shared hosting environment, sorry but this isn’t for you. If you want to jump in managing your own VPS, I highly recommend DigitalOcean, cheapest and fastest that I’ve known so far, the one I currently use for all my sites.

The following applications, nginx, php-fpm, redis and predis, a flexible php client library for redis.

Finally, at least a very basic knowledge in using linux based systems. I prefer using CentOS and the server that this blog is on is using this distro.

Setting up Redis

If you’ve got all three covered, let’s get started. This redis config works well with this centos + nginx + php-fpm installation in a vps. Install nginx and php-fpm, make sure it’s configured properly to run WordPress.

Install redis on your server, here’s a clean and easy to follow guide for installing redis on CentOS. Make sure Redis is running on your server before continuing.

Setup a WordPress blog on your vps. While I don’t have a good guide yet based on my setup, I recommend you read this guide to get you started.

Installing WP Index Redis

Please download wp-index-redis.php, you’ll need this to interface WP with Redis.

I really don’t have a name for this script but, I’ll just call it it WP Index Redis for now. I’ve made a lot of changes to the script that Jim originally created but here are the main difference:

  • Pages are not cached when you are logged in.
  • Cached pages do not expire not unless explicitly deleted or reset (deleting the entire domain cache).
  • Appending a ?c=y (e.g. domain.com/?c=y) to a url deletes the entire cache of the domain. Only works when you are logged in.
  • Appending a ?r=y to a url deletes the cache of that url.
  • Refreshing (F5) a page deletes the cache of that page.
  • Script still works even if allow_fopen is disabled in php.
  • Submitting a comment deletes the cache of that page.
  • Includes a debug mode, stats are displayed at the bottom most part after </html>. Won’t be deleted by CloudFlare.

Like always, use this at your own risk. Be comforted though that I use this myself on this very blog. So on to installing the redis cache interface:

Edit wp-index-redis.php and change some variables where appropriate. The default should work out of the box, but if you are using CloudFlare set $cf = 1, and if you’d like to see some cache generation times set $debug = 1. Otherwise leave everything as it is.

Upload wp-index-redis.php on your WP blog. Make sure you upload this where your main WordPress index.php resides.

Do the same for predis.php.

Rename the WordPress index.php to _index.php or any name that will indicate that this is the original index.php.

Rename wp-index-redis.php to index.php. That’s it you are all set.

Open your browser and access your site, if you’ve enabled debug, you should see some page execution status and figures at the bottom most part of each WordPress page.

For the curious, this blog is on CentOS with nginx + php-fpm + mysql + redis. I didn’t bother (for now) to install apc or xcache because I believe in the mantra, less is more. In the very near future I should be able to publish a guide on how I’ve setup my vps.

If nothing broke on your site, you can breathe now, congratulate yourself and let your neighbor know that your level of geek has just plus-oned (+1) today. ^_^

 

{ 46 comments… read them below or add one }

Ovidiu October 25, 2012 at 8:48 pm

just curios if this is strictly nginx only or can I use it for apache too?
saw the original article where you got the original script from, there were a few suggestions to turn this into a plugin or an object cache plugin, any chance that is going to happen?

Ovidiu October 26, 2012 at 2:42 am

I tried this on Apache and am not gettign anywhere. is this nginx only?

see errors:
[Thu Oct 25 16:40:36 2012] [warn] [client 108.162.222.170] mod_fcgid: stderr: PHP Fatal error: Uncaught exception ‘Predis\\ServerException’ with message ‘unknown command ‘HEXISTS” in /var/www/clients/client1/web3/web/predis.php:562
[Thu Oct 25 16:40:36 2012] [warn] [client 108.162.222.170] mod_fcgid: stderr: Stack trace:
[Thu Oct 25 16:40:36 2012] [warn] [client 108.162.222.170] mod_fcgid: stderr: #0 /var/www/clients/client1/web3/web/predis.php(689): Predis\\ResponseErrorHandler->handle(Object(Predis\\Connection), ‘ERR unknown com…’)
[Thu Oct 25 16:40:36 2012] [warn] [client 108.162.222.170] mod_fcgid: stderr: #1 /var/www/clients/client1/web3/web/predis.php(1374): Predis\\ResponseReader->read(Object(Predis\\Connection))
[Thu Oct 25 16:40:36 2012] [warn] [client 108.162.222.170] mod_fcgid: stderr: #2 /var/www/clients/client1/web3/web/predis.php(1383): Predis\\Connection->readResponse(Object(Predis\\Commands\\HashExists))
[Thu Oct 25 16:40:36 2012] [warn] [client 108.162.222.170] mod_fcgid: stderr: #3 /var/www/clients/client1/web3/web/predis.php(202): Predis\\Connection->executeCommand(Object(Predis\\Commands\\HashExists))
[Thu Oct 25 16:40:36 2012] [warn] [client 108.162.222.170] mod_fcgid: stderr: #4 /var/www/clients/client1/web3/web/index.php(66): Predis\\Client->__call(‘hexists’, Array)
[Thu Oct 25 16:40:36 2012] [warn] [client 108.162.222.170] mod_fcgid: stderr: #5 /var/www/clients/client1/web3/web/index.php(66): Predis\\Client->hexists(‘efa3ef8516319ee…’, ’2de6e35e8cc7eb6…’)
[Thu Oct 25 16:40:36 2012] [warn] [client 108.162.222.170] mod_fcgid: stderr: #6 {main}
[Thu Oct 25 16:40:36 2012] [warn] [client 108.162.222.170] mod_fcgid: stderr: thrown in /var/www/clients/client1/web3/web/predis.php on line 562

Jeedo October 30, 2012 at 1:45 am

Hi Ovidiu,

Yes this setup is currently for nginx. If you’ve setup redis properly though, it should run fine with no problems with apache (theoretically) since redis runs as a separate process.

As far as making this into a plugin, not sure yet as of this time. As far as my opinion is concerned I think this is the better implementation so far because no process is touched in WordPress once the redis cache is hit.

I’ll find a better means to implement this though soon as I have time.

With the errors you are encountering, I suggest you check your redis version and make sure you install the latest stable release. The messages you are getting suggests that the called function doesn’t exist.

PostBlue November 5, 2012 at 5:18 pm

Hello. I’m using Nginx with fastcgi_cache like in this tutorial : http://rtcamp.com/tutorials/wordpress-nginx-fastcgi-cache-purge-conditional/ Is your method compatble with this one, or is it replacement of such a method ? I would be glad if you can answer this, I’m just discovering the capabilities of Redis (I’m using it with etherpad-lite and haste-server for the moment, replacing sqlite for the first one and memcache for the second) and I want to make more use of it. Thank you for this post, I bookmarked it. :)

Jeedo November 10, 2012 at 6:23 am

Hi PostBlue,

Technically yes, but there’s nothing to gain from it. It’s a different approach and using redis would be redundant as both cache hits are called directly from memory. Theoretically, the approach you referred should be faster because pages are served directly by nginx and doesn’t even hit the wordpress layer. I’m using redis out of curiosity and because the fastcgi_cache_purge module isn’t installed on my vps.

Resende November 25, 2012 at 1:24 pm

Awesome post, Jeedo!

How to configure predis.php to listen to the Redis socket (not the port #) and avoid the TCP overhead? I guess that could improve the performance a bit further.

Jeedo November 25, 2012 at 1:36 pm

Hi Resende,

Thanks for the suggestion, I’ll look it up and see if I can improve this further. Been busy with work lately but I really want to make an update on this redis wordpress cache.

Resende November 25, 2012 at 2:20 pm

Thanks, Jeedo.

Maybe replacing DEFAULT_HOST = ’127.0.0.1′ for the absolute unix path of the socket would do the trick, but I’m not that sure.

Something I just noticed is that the cache is not serving CDN files (CloudFront in my case). It grabs them directly from my server. Is there any way to bypass that?

Albi December 1, 2012 at 11:22 pm

I’m getting the following errors after doing all the steps:

Warning: Unexpected character in input: ‘\’ (ASCII=92) state=1 in /home/wwwroot/domain.com/index.php on line 50

Parse error: syntax error, unexpected T_STRING in /home/wwwroot/domain.com/index.php on line 50

which corresponds to this line:

$redis = new Predis\Client(”);

Any ideas of what the problem might be?

Jeedo December 2, 2012 at 3:37 am

Hi Albi,

Please check your PHP version, I’m running version 5.4, if you are running anything less than 5.3, you might bump into problems. Check as well if you haven’t changed anything (after the variables) in the file by accident. It would also help if you let me know specifics about your setup like the OS, nginx version, redis version, etc.

Jeedo December 3, 2012 at 2:39 am

Hi Resende,

Haven’t tried using unix sockets yet with redis probably will in the near future when I try out different configurations with nginx. I’m not sure how CloudFront works but I currently use CloudFlare, and it works seamlessly with my current setup. With your current setup, it should work theoretically though because what redis simply does is serve a static (cached) page from a server’s memory.

k0nsl December 12, 2012 at 5:29 pm

Hi,

Very interesting. I didn’t know much ’bout Redis prior to this.
I’ll give it a try later in the day (I will test connecting via unix socket, too).

Thanks.

-k0nsl

Jeedo December 17, 2012 at 6:22 pm

Hi k0nsl,

Thanks for dropping by, please let us know how it goes on your end. I hope your redis adventure will turn out without a hitch.

afk4life January 10, 2013 at 4:41 am

This solution works great except on ajax-submitted form (Contact Form 7). With redis caching (no W3TC or Varnish) the form won’t submit, it just spins and spins but never submits. Any ideas?

Jeedo January 10, 2013 at 4:43 pm

Hi afk4life,

Kindly mail me the URL where you installed Contact Form 7. As far as my experience goes as long as all the ajax and javascript libraries have been loaded, there shouldn’t be any conflict with redis since it’s just a front-end cache.

Safia January 14, 2013 at 9:48 pm

Hi great tutorial

I’ve just had my hosting install this for me, and it is coming up with a few errors.
First off i have a WP Multisite installation and was wondering if this works on my install

Thanks

Jeedo January 16, 2013 at 3:57 pm

Hi Safia,

It would help if you let us know what the errors are. We might find a fix for it.

Chris March 9, 2013 at 7:34 pm

After looking at some of the GET/SAVE functions of PHP Cache Lite, it looks simular to what the redis script does. https://pear.php.net/package/Cache_Lite/docs

Do you think that using Cache_Lite to checking/serving cache could be possible without the need for a 3rd party engine like redis, or an existing plugin like W3 Total Cache? Would there be any benefit to using PHP itself for caching?

Thanks,
Chris

Jeedo March 12, 2013 at 3:44 am

Hi Chris,

Sorry not very familiar with cache_lite, I’d say because of the similarity, you just choose one or the other depending on which one you prefer and the way your server is setup. I use redis because it’s fast (memory store no disk read/writes) and I don’t need to add any third party plugin on my current WordPress install.

Pooya March 22, 2013 at 12:59 pm

Hi,
I have the same problem as afk4life. how did you fixed it?

url: 1chatroom.ir/email/

Jeedo April 2, 2013 at 7:08 pm

Hi Pooya,

Unfortunately this redis cache won’t work with any plugin that alters content on the fly. I’ll try to find a work-around this one but I don’t think I’ll be doing that soon.

Franky April 3, 2013 at 11:00 am

Hi Jeedo,

did you ever tried this installation with a VPS and a Multisite Installation or more than one site running on the server?

Jeedo April 5, 2013 at 1:22 am

Hi Franky,

Unfortunately I haven’t tried it with wpmu yet. Just on a VPS with a single site wordpress install. I have two sites specifically on redis on a single VPS.

nicholas April 24, 2013 at 7:40 am

Hey there, what do I need to change on the redis files if REDIS is running on a multi server configuration? We have 2 front end servers, 1 db server, 1 NFS server for the content and a load balancer.

nicholas April 24, 2013 at 9:20 am

Can we use

$redis = new Predis\Client(array(
‘scheme’ => ‘tcp’,
‘host’ => ’10.0.0.1′,
‘port’ => 6379,
));

to load specific server where REDIS works?

Jeedo May 28, 2013 at 11:02 pm

Hi nicholas,

I’m no redis expert, what I can suggest is emulate a particular scenario and see if it works. I’ve always went to the trial and error route whenever I need to prove if a solution is viable.

Jeedo May 28, 2013 at 11:05 pm

Hi nicholas,

Not sure what your particular purpose is for running redis, but I suggest you read the documentation: http://redis.io/documentation, as I’m no redis expert unfortunately.

Erik July 10, 2013 at 10:41 am

Hey Jeedo,

I was wondering if you know about how many people have shown interest in this project? Do you have quite a few people submitting questions and interest in a WrodPress redis plug in?

Cheers!

Erik

shawn August 7, 2013 at 5:18 am

Do you happen to know why when I turn debug on, every page says ‘cache of page deleted 0.0xxx’

The first time I went to a page, it said it was a cached page and was in the 0.001xx range, but every time since then I always see ‘cache of page deleted’

Does that mean it is not working, or I need to change something?

Michael Cabral Poubel Bastos September 12, 2013 at 6:22 am

http://michaelbastos.com/wp-chef-now-supports-redis-deployment-for-wordpress/ I’ve included Redis in WP-Chef thanks to Eric Mann’s Tutorial…

Hans Kuijpers September 16, 2013 at 8:44 pm

I did a rewrite on your version to connect with PHPRedis instead of PRedis.
Furthermore I switched $dkey and $ukey to prevent huge Keys.

see my version on Github
https://gist.github.com/hans2103/5657621

regards,
Hans

Abdes October 12, 2013 at 10:02 pm

Hello Jeedo,

First thanks for the tips, it’s working very well !

I only have one problem on my cart page (Woocommerce)

I wanted to avoid the cache of this page but I don’t know how to use : Appending a ?r=y to a url deletes the cache of that url.

When I added ?r=y to the end of my “cart” page it’s automatically change to cartry…

Could you please tell me how to set this ?r=y parameter
Thanks in advance ;)

Frank December 8, 2013 at 9:30 am

Jeedo,

can you help me setup Redis on my site? Please I’m a Filipino kababayan

Jeedo December 16, 2013 at 2:55 am

Hi Frank, sorry for the late reply, been awfully busy with work, I can try kindly post details of your setup in this thread.

Jeedo December 16, 2013 at 2:58 am

Hi Abdes, I’m afraid I don’t have a solution for that right now, I may need to create a configurable system that can exclude specific folders.

Jeedo December 16, 2013 at 2:59 am

Hi Hans, thanks for improving. I’ve been out and about, I do hope I get the chance to sit down this Christmas and work on this.

Jeedo December 16, 2013 at 3:00 am

Hi Michael, thanks for the share, very helpful for those using wp-chef.

Jeedo December 16, 2013 at 3:01 am

Hi shawn, not too sure about this, could be a bug, I’ll try if I can replicate it on one of my sites.

Jeedo December 16, 2013 at 3:02 am

Hi Erik, just the people you are seeing on this comment thread, not a lot, its really a niche system but it could be useful for people prioritizing speed and performance on their WordPress blogs.

Russ P. Floyd December 29, 2013 at 2:23 pm

I am using Dwoo template http://dwoo.org/ for my PHP project, and Dwoo allow me to create custom plugins. each plugin is stored inside php file, and Dwoo will be the one that handle the plugin lazy-loads.

Jeedo January 2, 2014 at 6:17 am

Hi Russ, definitely looks promising, I’m looking for a templating engine for one of my projects might consider this.

John Elekman January 3, 2014 at 9:36 pm

Hi Jedo, there is an issue that I have raised with other people that have proposed this solution and nobody has come up with a fix.

The issue is with comment form. From time to time the comments form tend to load cached details of another user and so exposing somebody else details including emai addressl. Is there a way to tell this setup not to cache form content? This is a big issue on traffic with a lot of people post tons of comments hourly.

Patrick January 20, 2014 at 11:01 am

This script looks great. I already implemented the version from Jim Westergreen, but I do see some optimizations here.

However, I was wondering whether this script would also work well on a Wordpress blog with a mobile theme? Whenever I use a mobile theme, both desktop and mobile users see different versions of the webpages. I would like to disable Redis for mobile agents and only enable it for desktop users.

Hiroshi M March 23, 2014 at 4:29 am

Hi, jeedo. I’m very happy to know your site and this article!!
Perfomance of my wordpress site improve very very!!! faster!!
Thanks a lot!

So, I translate the installation to Japanese. The url is here.
http://beautifulajax.dip.jp/?p=667

Jeedo March 30, 2014 at 2:32 pm

Hi Hiroshi,

Thanks for the translation :-)

I’m glad it helped speed up your site.

Jeedo March 31, 2014 at 1:39 am

Hi John,

Thanks for the heads-up, this is indeed a bug or rather a limit to the system. I’ll need a plugin to remove the stale cache entries. My schedule’s still packed but definitely one of my to-dos for this system.

Leave a Comment

Previous post:

Next post: