Skip to content

Instantly share code, notes, and snippets.

@JimWestergren
Last active February 11, 2023 18:28
Show Gist options
  • Star 38 You must be signed in to star a gist
  • Fork 19 You must be signed in to fork a gist
  • Save JimWestergren/3053250 to your computer and use it in GitHub Desktop.
Save JimWestergren/3053250 to your computer and use it in GitHub Desktop.
Redis as a Frontend Cache for WordPress
<?php
/*
Author: Jim Westergren & Jeedo Aquino
File: index-with-redis.php
Updated: 2012-10-25
This is a redis caching system for wordpress.
see more here: www.jimwestergren.com/wordpress-with-redis-as-a-frontend-cache/
Originally written by Jim Westergren but improved by Jeedo Aquino.
some caching mechanics are different from jim's script which is summarized below:
- cached pages do not expire not unless explicitly deleted or reset
- appending a ?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
- submitting a comment deletes the cache of that page
- refreshing (f5) a page deletes the cache of that page
- includes a debug mode, stats are displayed at the bottom most part after </html>
for setup and configuration see more here:
www.jeedo.net/lightning-fast-wordpress-with-nginx-redis/
use this script at your own risk. i currently use this albeit a slightly modified version
to display a redis badge whenever a cache is displayed.
*/
// change vars here
$cf = 1; // set to 1 if you are using cloudflare
$debug = 0; // set to 1 if you wish to see execution time and cache actions
$display_powered_by_redis = 1; // set to 1 if you want to display a powered by redis message with execution time, see below
$start = microtime(); // start timing page exec
// if cloudflare is enabled
if ($cf) {
if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP'];
}
}
// from wp
define('WP_USE_THEMES', true);
// init predis
include("predis.php");
$redis = new Predis\Client('');
// init vars
$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 isn't a comment submission
(isset($_SERVER['HTTP_CACHE_CONTROL']) && $_SERVER['HTTP_CACHE_CONTROL'] == 'max-age=0') ? $submit = 1 : $submit = 0;
// check if logged in to wp
$cookie = var_export($_COOKIE, true);
$loggedin = preg_match("/wordpress_logged_in/", $cookie);
// check if a cache of the page exists
if ($redis->hexists($dkey, $ukey) && !$loggedin && !$submit && !strpos($url, '/feed/')) {
echo $redis->hget($dkey, $ukey);
$cached = 1;
$msg = 'this is a cache';
// if a comment was submitted or clear page cache request was made delete cache of page
} else if ($submit || substr($_SERVER['REQUEST_URI'], -4) == '?r=y') {
require('./wp-blog-header.php');
$redis->hdel($dkey, $ukey);
$msg = 'cache of page deleted';
// delete entire cache, works only if logged in
} 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 {
// turn on output buffering
ob_start();
require('./wp-blog-header.php');
// get contents of output buffer
$html = ob_get_contents();
// clean output buffer
ob_end_clean();
echo $html;
// Store to cache only if the page exist and is not a search result.
if (!is_404() && !is_search()) {
// store html contents to redis cache
$redis->hset($dkey, $ukey, $html);
$msg = 'cache is set';
}
}
$end = microtime(); // get end execution time
// show messages if debug is enabled
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 the: 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.jimwestergren.com/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>";
}
// time diff
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);
}
?>
@JimWestergren
Copy link
Author

Just changed the following:

// store html contents to redis cache
$redis->hset($dkey, $ukey, $html);
$msg = 'cache is set';

To this:

// Store to cache only if the page exist. Otherwise status code 200 is sent when 404 should be sent.
if (!is_404()) {
    // store html contents to redis cache
    $redis->hset($dkey, $ukey, $html);
    $msg = 'cache is set';
}

This is because I noticed Google indexing 404 pages as no 404 header was sent.

@sysrenan
Copy link

Hey Jim, I'm wondering if there's a way to only clean the cache on Reload (F5) or any type of refresh only if the user is logged in. Might be a bad idea to clear cache of all other users that does a reload. I'm looking at the index.php but it might not be there, it might be on the predis.php file but I couldn't yet figure out how to go by this. Let me know if you have any idea. Great work!

@JimWestergren
Copy link
Author

@sysrenan
You can make that work if you change line 75 from:
} else if ($submit || substr($_SERVER['REQUEST_URI'], -4) == '?r=y') {

To this:
} else if ($loggedin && ($submit || substr($_SERVER['REQUEST_URI'], -4) == '?r=y')) {
But I do not recommend that as the cache of the page will not be purged when non-logged in are commenting. So they won't see their own new comment and others will not as well.

@JimWestergren
Copy link
Author

Line 114 changed from:
if (!is_404()) {

To:
if (!is_404() && !is_search()) {

@sysrenan
Copy link

On your above code the reaction I got was that when doing a Reload, the cache is being set instead of just showing the cached page. For now at least it's not deleting the cache.

Also, I had to do the following change:

Line 61 changed from:
(($_SERVER['HTTP_CACHE_CONTROL'] == 'max-age=0') ? $submit = 1 : $submit = 0);

To:
// only check Cache-Control if it is set
if(isset($_SERVER['HTTP_CACHE_CONTROL'])){
if($_SERVER['HTTP_CACHE_CONTROL'] == 'max-age=0'){
$submit = 1;
} else {
$submit = 0;
}
} else {
$submit = 0;
}

Had to change it because I was getting these errors (dev machine so have to display all erros in php):
Notice: Undefined index: HTTP_CACHE_CONTROL in /var/www/html/index.php on line 61

Thanks.

@JimWestergren
Copy link
Author

Thanks @sysrenan,
I have now changed so that line 61 is:
(isset($_SERVER['HTTP_CACHE_CONTROL']) && $_SERVER['HTTP_CACHE_CONTROL'] == 'max-age=0') ? $submit = 1 : $submit = 0);
It is the same as you wrote :)

@k0nsl
Copy link

k0nsl commented May 14, 2013

Hi Jim (or should I say "hej" hehe)!

Do you know if this script is compatible with PHP 5.4.14? I tried it briefly on my installation but I kept getting HTTP Error 500 (Internal Server Error) all the time despite trying to set absolute paths and whatnot. The logs do not tell anything, yet.
Redis server is installed and functional (tried a test query to it).

I'm very interested in trying this :)

Faithfully,
Kuensl

@lonetwin
Copy link

hey @k0nsl, @JimWestergren line 61 has a syntax error in the current revision (ie: rev 11) of this gist -- a extra ')' at the end

@JimWestergren
Copy link
Author

Thanks @lonetwin, corrected!

@hans2103
Copy link

hans2103 commented Jun 6, 2013

Why show both $display_powered_by_redis and $debug? They show the same message.

I've changed a couple of things on my implementation since we're using phpredis instead of predis.

My changes regarding debug information.
delete line 34
$display_powered_by_redis = 1;

replace line 124 - 134 with
// show messages if debug is enabled
if ($debug && in_array($ip, $allowed)) {
// You should move this CSS to your CSS file and change the: float:right;margin:20px 0;
// Mind that if the page is served through Cloudflare you might view a cached page from Cloudflare.
echo "<style>#redis_powered{margin:20px auto;background:url(http://images.staticjw.com/jim/3959/redis.png) 10px no-repeat #fff;border:1px solid #D7D8DF;padding:10px;width:240px;}
#redis_powered div{text-align:center;font:10px/11px arial,sans-serif;color:#000;margin-left:100px;}</stylehttps://drupal.org/project/redis";
echo "<a href='http://www.byte.nl/blog/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<br/>".$msg."</div></div></a>";
}

as you can see... I've implemented a new variable -> $allowed
you are allowed to see the Redis debug information only if your IP-address is in the allowed array:
// allow IP-addresses for debug information
$ip = $_SERVER['REMOTE_ADDR'];
$allowed = array(
'255.255.255.255', // ip address of Joe Example @ Home
'1.1.1.1', // ip address of Joe Example @ Work
);

Mind that when your page is server through Cloudflare you might view a cached page from Cloudflare. This can be annoying if you just forgot. ;-)

@boniface
Copy link

Hey Jim, thanks for this and really make WP fly. However, I have noticed a couple of issues 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 email. Is there a way to tell this setup not to cache form content?

@postme
Copy link

postme commented Aug 3, 2013

Hi, I've made a small adaptation to be able to deal with the distinction between caching plain and ssl pages

if (is_ssl()) {
  $url = "https://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
} else {
  $url = "http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}

I've used the WordPres is_ssl() function for this

@erdihu
Copy link

erdihu commented Feb 27, 2014

Hi,
This is just a great work. Many thanks to all who contributed.
However, any ideas how to enable auto purge page caches after X seconds for example?

Some ideas to have more control on caching:

  1. Ability to exclude certain pages from cache.
  2. Ability to set different auto purge times to different pages.

I believe using widgets would be easier if these options implemented.

@oneinstack
Copy link

I would love to know how I can make the post view(wp-postviews) counts bypass the cache.

@kevin25
Copy link

kevin25 commented Nov 9, 2016

How can we exclude cart and checkout from Woocommerce?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment