Jim Westergren
A blog about me, my projects, SEO, Web Development and Personal Development.
"If we did all the things we are capable of, we would literally astound ourselves." - Thomas A. Edison

How to load WordPress in a few milliseconds using Redis

WordPress plus Redis = Love

Redis is an advanced key-value store. Like memcached on steroids. Everything is in the RAM and you can theoretically reach 100 000 GET per second with Redis.

My solution below will cache all the HTML output in Redis and display it without the need to load WordPress.

A big thanks to Jeedo Aquino who have rewritten and improved my original solution. Details here. This article updated on the October 25, 2012 with the new code.

If you don’t already have your own VPS or server – do these steps first: Guide: WordPress with Maximum Performance and Speed.

Installing Redis

If you are using Debian then just:

apt-get install redis-server

Or else, just see the installation instructions.

Using Predis as a PHP client for Redis

You need a client in order to use PHP to communicate with Redis.

I recommend Predis. Upload predis.php to the WordPress root directory. See more about Predis on Github (if you use a newer version of Predis you need also to modify the script below).

Installing the PHP script for the Frontend Cache

Step 1:

Add the code below to a new file called index-with-redis.php and place it in the WordPress root directory.

Step 2:

Optional: modify the 3 variables at the top of the file.

Step 3:

If you are using Apache change all occurances of index.php to index-with-redis.php in the .htaccess.

If you are using the more preferred nginx then you need to rename index-with-redis.php to index.php. Unfortunately this also means that you have to replace this file each time you upgrade WordPress. You can also change nginx.conf to access index-with-redis.php instead of index.php but it creates some bugs and I would avoid that.

Step 4:

Deactivate all other caching plugins.

Instructions and info:

Benchmark for this blog:

I have been using this setup for this blog for quite some time now and all has been working well.

Some other advice

I am using the following setup for this blog: Nginx + PHP-FPM + APC + Cloudflare + Redis. Hosted on a nano VPS with 6sync for $15 per month. No caching plugins. It is maybe the fastest way to serve WordPress today. See here all the steps I did to setup this.

Make sure you also send HTML as gzip to the browser. You can do that in the .htaccess (Apache), nginx.conf (Nginx) or via CloudFlare.

See also my article: Why I recommend CloudFlare and why you should try it too

Please comment

I would love to hear from you!

Boomark This! Subscribe to the RSS feed
About the Author Jim Westergren Jim Westergren is a web entrepreneur from Sweden now living in Bolivia. He is happily married and has two lovely children. Some of his interests are web development, SEO and writing. He is the Founder and CTO of TodaysWeb and his current major project is N.nu. Read his . Follow Jim on Twitter or Google+.
  • http://www.shareyouressays.com Webmaster

    Jim, thanks for the post!

    Aren’t you using any caching plugin like super cache or w3 total cache?

  • http://markjaquith.com/ Mark Jaquith

    A few notes: You could install this as a WordPress advanced-cache.php drop-in, and then the only change you’d have to make is to define WP_CACHE in your config. It’s also a bit inefficient in that uncached views actually generate two dynamic page views (as you’re “forking” a separate HTTP request to populate the cache). What might be better is to unset the cookie, start an output buffer, and then write to reddis at the end.

    Even better, you could write a Reddis-backed object cache drop-in, and then just use Batcache as the caching engine (but using Reddis instead of Memcached or APC).

  • http://www.jimwestergren.com/about-me-jim-westergren/ Jim Westergren

    Aren’t you using any caching plugin like super cache or w3 total cache?

    No, no cache plugin being used.

    Mark,
    Thank you for the suggestions. But when is advanced-cache.php being loaded? After a bunch of includes or maybe some SQL queries? My solution above does not load any other thing if there is cache.

    Yes, uncached views generate 2 HTTP requests. But as the cache is 7 days and only cleared if there is a comment it should not be so often. But yes, cookies might be a good idea. I have limited time now but will check it out tomorrow. I will also check out about object cache drop-in.

  • Adam B

    Man, that’s fast! Could this be a potential optimization?

    Switch this:

    } else if ($redis->exists($redis_key)) {

    to:

    } else if ($html_of_current_page = $redis->get($redis_key)) {

    I’m not sure how the Redis class is written, but I’d presume that it’d have to make two separate requests to the Redis server in the case of a successful fetch, rewriting this might make it so you’d only need one request.

  • http://annonseragratis.net Stefan Johansson

    Very nice!

    I tried this on my server with Nginx and it work very well, except for my site annonseragratis.net. When I go to the startpage annonseragratis.net I get an 500 internal server error. If I go to another page, post or category, it works like a charm. Can you tell me why the startpage gives me the error?

  • Khuram Malik

    I’m intrigued, but could you please help me to understand that if you’re using Redis to cache the html output then why do you need CloudFlare?

    Are you using CloudFlare strictly as a CDN and to serve static content such as PNG files and CSS files etc, or do you like it because of the DNS pre-fetching and reverse-proxying capabilities?

    I’d be interested to hear further from you. I might consider something like this myself.

  • http://annonseragratis.net Stefan Johansson

    I made a mistake at my site that generated the 500 internal server error. Now it works, but I recognized a couple of other problems. When I have logged in it still looks that I’m not logged in, but when I went to my profil page I’m logged in. Also the social log in using Facebook and Twitter not work and give the 500 internal server error. I have the same problem with another site were I’m using BuddyPress. Any idea about what causing this?

  • Dan

    This is fantastic. Seems very easy to configure, especially if this is simplified into an advanced-cache.php drop-in. Looking forward to see what can be done with Redis.

  • http://cutecaptions.com Benjamin Adams

    Wow, I am super impressed on how fast my site is loading now. Great job!!!

    Two things,
    Would this be a bad idea to use on a wordpress site with 330k posts?

    It appears the root index page of the site is not being cached

  • http://drewclardy.com Drew Clardy

    For some reason, this is not working for me. I have created predis.php, index-with-redis.php. I have set the document root to index-with-redis.php, and I have set the rewrite rules to go through there as well.

    location / {
    try files $uri $uri /index-with-redis.php
    }

    I am getting this error in my error logs.
    2012/07/06 18:07:56 [error] 16879#0: *1 directory index of "/var/www/example.com/web/wp-admin/" is forbidden, client: 12.6.128.8, server: drewclardy.com, request: "GET /wp-admin/ HTTP/1.1", host: "example.com"

    Here is my full server config for the site.

    server {
    listen *:80;

    server_name example.com http://www.example.com;

    root /var/www/example.com/web;

    index index.html index.htm index-with-redis.php index.cgi index.pl index.xhtml;

    error_page 400 /error/400.html;
    error_page 401 /error/401.html;
    error_page 403 /error/403.html;
    error_page 404 /error/404.html;
    error_page 405 /error/405.html;
    error_page 500 /error/500.html;
    error_page 502 /error/502.html;
    error_page 503 /error/503.html;
    recursive_error_pages on;
    location = /error/400.html {
    internal;
    }
    location = /error/401.html {
    internal;
    }
    location = /error/403.html {
    internal;
    }
    location = /error/404.html {
    internal;
    }
    location = /error/405.html {
    internal;
    }
    location = /error/500.html {
    internal;
    }
    location = /error/502.html {
    internal;
    }
    location = /error/503.html {
    internal;
    }

    error_log /var/log/ispconfig/httpd/example.com/error.log;
    access_log /var/log/ispconfig/httpd/example.com/access.log combined;

    ## Disable .htaccess and other hidden files
    location ~ /\. {
    deny all;
    access_log off;
    log_not_found off;
    }

    location = /favicon.ico {
    log_not_found off;
    access_log off;
    }

    location = /robots.txt {
    allow all;
    log_not_found off;
    access_log off;
    }
    location /stats {
    index index.html index.php;
    auth_basic "Members Only";
    auth_basic_user_file /var/www/clients/client1/web1/.htpasswd_stats;
    }

    location ^~ /awstats-icon {
    alias /usr/share/awstats/icon;
    }

    location ~ \.php$ {
    try_files $uri =404;
    include /etc/nginx/fastcgi_params;
    fastcgi_pass 127.0.0.1:9010;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_script_name;
    fastcgi_intercept_errors on;
    }

    }

    Any help would be greatly appreciated.

  • http://annonseragratis.net Stefan Johansson

    I have tested to run this again at my server, and now is the social log in working. I still have the issue with login not showing that I’m logged in at a couple of sites. I will keep this running now and se if I can fix that problem by my self.

  • peter black

    surry guys but why dont you use varnish or squid as a real frontend cache?

  • http://annonseragratis.net Stefan Johansson

    No I have recognized a new problem. When I empty the cache with “?refresh=yes” I got a couple of sites were the frontpage is getting blank. Other sides and posts are working correctly, except from the frontpage. I also tried to “flushdb” through “redis-cli” bout the frontpage I still blank.

    Any idea what this is caused by?

  • http://www.jimwestergren.com/about-me-jim-westergren/ Jim Westergren

    Hello all. I am sorry for not getting back to you sooner. In my case I also noticed a problem with the RSS not being updated on this blog but that can also relate to Feedburner.

    Regarding not being seen as logged in – it can be avoided by checking for the cookie. I didn’t do that as I want to see the cache for myself as well.

    In any case, there seem to be a few issues left to fix. Perhaps in the meantime you should use the W3 Total Cache plugin for the moment. Activate APC and I think Varnish is the best for the moment. At least until my solution is more mature.

    I am also very busy with other things at the moment.

  • http://grenfeldt.com Jonas

    Thanks for this one Jim, we’re gonna try it out on some of our sites!

  • DSchragg

    This sounds pretty awesome, I’m certainly going to try this out. I’m currently using Varnish with Magento, and it has drasticly improved load times, but it has its issues.

    I’m confused how you have Nginx, PHP-FPM, APC, Redis, and WordPress all working within 256MB RAM?

    Also, do you know of any performance differences between using Redis as a service vs. compiling the Nginx addon?

  • http://www.mvision.se/ Måns Gullberg

    This combined with cloudflare is the killer… :)

  • http://www.wpshouter.com/ Arafin Shaon

    Great article! I learned things that I never knew before. Thank you for sharing this helpful article with us.

  • Ben Adams

    I must say, I’ve been using this on cutecaptions.com now for awhile and it has worked flawlessly!! Thanks so much for introducing me to redis.

  • Azman0101

    Hi,

    What’s the difference between your solution and php5-redis module that it can be used for save php session:


    php.ini

    ;session.save_handler = files

    session.save_handler = redis
    session.save_path = "tcp://127.0.0.1:6379?weight=1"

    thxs

  • http://www.grosirlulurbali.com Lulur Bali

    Thanks for give me informations… I’d better bookmarking your sites right now…

  • http://www.jeedo.net/ Jeedo

    @Ben Adams

    Hi Ben, was wondering if I can get in touch with you and look at your setup. The cache results I’m getting are blank, is APC required for this to run?

    Cheers,

    Jeedo

  • http://www.jeedo.net/ Jeedo

    Hi Folks,

    Just wanted to let you guys know that I’ve made another version of Jim’s script here:

    http://www.jeedo.net/lightning-fast-wordpress-with-nginx-redis/

    The new version should work right out of the box and is slightly different in terms of the caching implementation used.

    Mad respects to Jim Westergreen for making me realize how awesome Redis is and for the inspiration as well.

  • http://www.jimwestergren.com Jim Westergren

    Hi Jeedo,

    Thank you for your contributions, our Skype conversation and the permission to publish the code here.

    I have now updated this article and also published another one on how to setup the VPS.

  • ovizii

    I followed the exact instructions as to the newly updated post but I get an error 500 and found this in my error log. running apache btw.

    [Thu Oct 25 23:55:04 2012] [warn] [client 108.162.222.182] 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 23:55:04 2012] [warn] [client 108.162.222.182] mod_fcgid: stderr: Stack trace:
    [Thu Oct 25 23:55:04 2012] [warn] [client 108.162.222.182] 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 23:55:04 2012] [warn] [client 108.162.222.182] mod_fcgid: stderr: #1 /var/www/clients/client1/web3/web/predis.php(1374): Predis\\ResponseReader->read(Object(Predis\\Connection))
    [Thu Oct 25 23:55:04 2012] [warn] [client 108.162.222.182] mod_fcgid: stderr: #2 /var/www/clients/client1/web3/web/predis.php(1383): Predis\\Connection->readResponse(Object(Predis\\Commands\\HashExists))
    [Thu Oct 25 23:55:04 2012] [warn] [client 108.162.222.182] mod_fcgid: stderr: #3 /var/www/clients/client1/web3/web/predis.php(202): Predis\\Connection->executeCommand(Object(Predis\\Commands\\HashExists))
    [Thu Oct 25 23:55:04 2012] [warn] [client 108.162.222.182] mod_fcgid: stderr: #4 /var/www/clients/client1/web3/web/index-with-redis.php(68): Predis\\Client->__call('hexists', Array)
    [Thu Oct 25 23:55:04 2012] [warn] [client 108.162.222.182] mod_fcgid: stderr: #5 /var/www/clients/client1/web3/web/index-with-redis.php(68): Predis\\Client->hexists('efa3ef8516319ee...', 'd4cc1783ee7a290...')
    [Thu Oct 25 23:55:04 2012] [warn] [client 108.162.222.182] mod_fcgid: stderr: #6 {main}
    [Thu Oct 25 23:55:04 2012] [warn] [client 108.162.222.182] mod_fcgid: stderr: thrown in /var/www/clients/client1/web3/web/predis.php on line 562

  • http://www.jimwestergren.com/ Jim Westergren

    That looks like Redis is not installed on the server. Try to connect to Redis with the redis-cli command.

  • Ovidiu

    Thanks Jim,redis is installed and running:

    myserver:/# redis-cli
    redis> HEXISTS
    Wrong number of arguments for ‘hexists’

    I restarted redis and now I get slightly different errors:

    [Sat Oct 27 07:05:50 2012] [warn] [client 108.162.222.183] 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

    [Sat Oct 27 07:05:50 2012] [warn] [client 108.162.222.183] mod_fcgid: stderr: Stack trace:

    [Sat Oct 27 07:05:50 2012] [warn] [client 108.162.222.183] mod_fcgid: stderr: #0 /var/www/clients/client1/web3/web/predis.php(689): Predis\ResponseErrorHandler->handle(Object(Predis\Connection), ‘ERR unknown com…’)

    [Sat Oct 27 07:05:50 2012] [warn] [client 108.162.222.183] mod_fcgid: stderr: #1 /var/www/clients/client1/web3/web/predis.php(1374): Predis\ResponseReader->read(Object(Predis\Connection))

    [Sat Oct 27 07:05:50 2012] [warn] [client 108.162.222.183] mod_fcgid: stderr: #2 /var/www/clients/client1/web3/web/predis.php(1383): Predis\Connection->readResponse(Object(Predis\Commands\HashExists))

    [Sat Oct 27 07:05:50 2012] [warn] [client 108.162.222.183] mod_fcgid: stderr: #3 /var/www/clients/client1/web3/web/predis.php(202): Predis\Connection->executeCommand(Object(Predis\Commands\HashExists))

    [Sat Oct 27 07:05:50 2012] [warn] [client 108.162.222.183] mod_fcgid: stderr: #4 /var/www/clients/client1/web3/web/index-with-redis.php(68): Predis\Client->__call(‘hexists’, Array)

    [Sat Oct 27 07:05:50 2012] [warn] [client 108.162.222.183] mod_fcgid: stderr: #5 /var/www/clients/client1/web3/web/index-with-redis.php(68): Predis\Client->hexists(‘efa3ef8516319ee…’, ’189e7dcf443683b…’)

    [Sat Oct 27 07:05:50 2012] [warn] [client 108.162.222.183] mod_fcgid: stderr: #6 {main}

    [Sat Oct 27 07:05:50 2012] [warn] [client 108.162.222.183] mod_fcgid: stderr: thrown in /var/www/clients/client1/web3/web/predis.php on line 562

    any other ideas?

  • Ovidiu

    Btw, on another server, running nginx I get it to work just fine, except when I’m logged in. While logged in I find this in my error logs:

    2012/10/27 03:34:38 [error] 11631#0: *86 FastCGI sent in stderr: “PHP Notice: Undefined index: HTTP_CACHE_CONTROL in /var/www/knightsenglish.com/index.php on line 61″ while reading response header from upstream, client: 173.245.62.120, server: knightsenglish.com, request: “GET / HTTP/1.1″, upstream: “fastcgi://127.0.0.1:9000″, host: “www.knightsenglish.com”, referrer: “http://www.knightsenglish.com/imersao-de-ingles/”

  • Jeedo Aquino

    hi Ovidiu,

    that’s a lot of errors if i might say. i’m not really sure why at this point of time but it would help if you can provide us your setup, currently, i’m using nginx + php-fpm + apc + redis on centos 6.2 x64.

    you might also want to check the redis version you are using. i’m using version 2.6.2. soon as i have time, i’ll include install instructions right from nginx to redis just like what jim did.

  • http://www.jimwestergren.com/ Jim Westergren

    The commmand HEXISTS is available in Redis since 2.0.0.
    http://redis.io/commands/hexists

    My guess is that you have an earlier version of Redis. Try to upgrade it.

  • Guest

    Does not work for me with apache. I did replace the filename in .htaccess, but if I rename index.php, it still does not find the new index file, and instead shows the whole folder as if there was no index file in it. Redis-cli works, apache has been restarted.

  • http://www.facebook.com/profile.php?id=1604796235 Alexander Sverdlov

    renamed the file to index.php, re-edited .htaccess. Works!

  • http://www.facebook.com/profile.php?id=1604796235 Alexander Sverdlov

    Alright, this is strange. Everything looks like it’s working on my site (Apache + redis), but on enabling debug, each time I load a page it shows different comments – “this is a cache”, “cache deleted”, etc. Isn’t it supposed to delete cache only manually? Also, with w3 total cache, the load time shown by gtmetrix was 2.7 seconds, with redis – 3.3 seconds? The plugins for caching are disabled.

  • http://www.jimwestergren.com/ Jim Westergren

    If you reload the page (f5) the cache is being deleted. Which one is your website?

  • http://www.jimwestergren.com/ Jim Westergren

    2 days ago I did a small update to the code so that it does not break the RSS feed.

    Changed from:
    if ($redis->hexists($dkey, $ukey) && !$loggedin && !$submit) {

    To:
    if ($redis->hexists($dkey, $ukey) && !$loggedin && !$submit && !strpos($url, ‘/feed/’)) {

  • Ovidiu

    This is weird. I seem to have redis-server version: Server started, Redis version 1.2.6 as that seems to be the default Debian Squeeze version.

    I then proceeded and installed the current one from the Debian backports:

    apt-get -t squeeze-backports install redis-server

    now running: Redis version 2.4.15 which should solve the problem :-)

  • Jeedo Aquino

    Hi Ovidiu,

    I’ll be releasing some updates that will address this, plus some minor improvements as well. I’ll let you guys know by tomorrow.

  • Larry

    Jim, if I hear you right, you are using nginx and redis, without apc and without varnish?
    I disabled w3tc and varnish and put in redis with your info above, but the site is a bit slower.
    Maybe I missed something…I have a lot of 3rd party calls – facebook, twitter, google analytics and so on, and they slow things down, but overall redis doesn’t give me that 0.000025 sec load time you indicated.

  • http://www.jimwestergren.com/ Jim Westergren

    I am using APC which makes a big difference. But not Varnish. The third party calls you are referring to are supposed to happen after the website has been loaded and should not count in the time it takes for Redis to generate the page.
    But try activating APC and see what happens.

  • http://www.vectorpile.com VectorPile

    My test setup of this works well and is lightning fast as promised, except that it caches things it shouldn’t. I have view counts on each of my posts and unfortunately these are not being updated. My current setup is nginx + eaccelerator + memcached, and it works fairly well, with a average page generating time of 0.15 – 0.2, which is still lot faster than a vanilla apache setup.

    I would love to know how I can make the post view counts bypass the cache.

  • http://www.jimwestergren.com/ Jim Westergren

    Perhaps with a str_replace on the $html before you echo it?

Previous post in category:

Next post in category:

Design, text and custom cache solution by myself.

Page generated in
0.00357 sec