EN / ZH
Using Redis Cache to Accelerate Your WordPress Site

Redis is an advanced key-value storage system, similar to memcached, where all content resides in memory, enabling over 100,000 GET operations per second.

The solution I propose below caches all output HTML in Redis, eliminating the need for WordPress to re-execute page scripts. Using Redis instead of Varnish is simpler to set up and faster.

Using Redis Cache to Accelerate Your WordPress Site

Installing Redis

If you’re running Debian or a derivative, install Redis with:
apt-get install redis-server

Using Predis as the PHP Client for Redis

You need a client library for PHP to connect to the Redis server.

I recommend Predis. Upload predis.php to your WordPress root directory.

Frontend Caching PHP Script

  1. Create a new file index-with-redis.php in your WordPress root directory with the following content:
<?php
$cf = 1; // Set to 1 if using Cloudflare
$debug = 0; // Set to 1 to see execution time and cache operations
$display_powered_by_redis = 1;  // Set to 1 to display Redis-powered message and execution time

$start = microtime(); // Start timing page execution

// If Cloudflare is enabled
if ($cf) {
    if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
        $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP'];
    }
}

// WordPress settings
define('WP_USE_THEMES', true);

// Initialize Predis
include("predis.php");
$redis = new Predis\Client('');

// Initialize variables
$domain = $_SERVER['HTTP_HOST'];
$url = "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$url = str_replace('?r=y', '', $url);
$url = str_replace('?c=y', '', $url);
$dkey = md5($domain);
$ukey = md5($url);

// Check if page is a comment submission
(isset($_SERVER['HTTP_CACHE_CONTROL']) && $_SERVER['HTTP_CACHE_CONTROL'] == 'max-age=0') ? $submit = 1 : $submit = 0;

// Check if logged into WordPress
$cookie = var_export($_COOKIE, true);
$loggedin = preg_match("/wordpress_logged_in/", $cookie);

// Check if page cache exists
if ($redis->hexists($dkey, $ukey) && !$loggedin && !$submit && !strpos($url, '/feed/')) {

    echo $redis->hget($dkey, $ukey);
    $cached = 1;
    $msg = 'this is a cache';

// If comment submitted or page cache clear requested, delete page cache
} else if ($submit || substr($_SERVER['REQUEST_URI'], -4) == '?r=y') {

    require('./wp-blog-header.php');
    $redis->hdel($dkey, $ukey);
    $msg = 'cache of page deleted';

// If logged-in user requests full cache clear, delete entire cache
} else if ($loggedin && substr($_SERVER['REQUEST_URI'], -4) == '?c=y') {

    require('./wp-blog-header.php');
    if ($redis->exists($dkey)) {
        $redis->del($dkey);
        $msg = 'domain cache flushed';
    } else {
        $msg = 'no cache to flush';
    }

// If logged in, don't cache anything
} else if ($loggedin) {

    require('./wp-blog-header.php');
    $msg = 'not cached';

// Cache the page
} else {

    // Start output buffering
    ob_start();

    require('./wp-blog-header.php');

    // Get output buffer contents
    $html = ob_get_contents();

    // Clean output buffer
    ob_end_clean();
    echo $html;

    // Only store to cache if the page exists and isn't a search result
    if (!is_404() && !is_search()) {
        // Store HTML content to Redis cache
        $redis->hset($dkey, $ukey, $html);
        $msg = 'cache is set';
    }
}

$end = microtime(); // Get execution end time

// If debug is enabled, display message
if ($debug) {
    echo $msg . ': ';
    echo t_exec($start, $end);
}

if ($cached && $display_powered_by_redis) {
    // You should move this CSS to your CSS file and change: float:right;margin:20px 0;
    echo "<style>#redis_powered{float:right;margin:20px 0;background:url(http://images.staticjw.com/jim/3959/redis.png) 10px no-repeat #fff;border:1px solid #D7D8DF;padding:10px;width:190px;}
    #redis_powered div{width:190px;text-align:right;font:10px/11px arial,sans-serif;color:#000;}</style>";
    echo "<a href=\"http://www.aips.me/wordpress-with-redis-as-a-frontend-cache/\" style=\"text-decoration:none;\"><div id=\"redis_powered\"><div>Page generated in<br/> " . t_exec($start, $end) . " sec</div></div></a>";
}

// Calculate time difference
function t_exec($start, $end) {
    $t = (getmicrotime($end) - getmicrotime($start));
    return round($t, 5);
}

// Get time
function getmicrotime($t) {
    list($usec, $sec) = explode(" ", $t);
    return ((float)$usec + (float)$sec);
}

?>

You can also view index-with-redis.php on GitHub.

  1. Replace the IP address and domain in the code above with your own website’s IP and domain.

  2. In .htaccess, change all occurrences of index.php to index-with-redis.php. If you’re using Nginx, modify index.php to index-with-redis.php in nginx.conf (and reload Nginx: killall -s HUP nginx).

Performance Benchmarks

  • Without Redis: Average homepage execution 1.614 seconds, post page 0.174 seconds (no caching plugins)
  • With Redis: Average page execution time 0.00256 seconds

I’ve been using this method on my blog for quite some time, and everything has been running smoothly.

Additional Recommendations

The author’s WordPress environment is Nginx + PHP-FPM + APC + Cloudflare + Redis, installed on a VPS with no caching plugins.

Make sure you have gzip compression enabled for faster access speeds.

Accessing wp-admin

To access wp-admin, use /wp-admin/index.php instead of /wp-admin/.

This article has been translated into Chinese before, but I noticed the original author keeps updating it while Chinese translators don’t bother to keep up, so I went and worked through it again myself.
English original: http://www.jimwestergren.com/wordpress-with-redis-as-a-frontend-cache/