Website Speed Part 3 – Caching WordPress

As so many people have it installed, I thought I would share with you a key techniques integral to caching WordPress that will help your website speed.

W3 Total Cache is a dream plugin for achieving caching, I would recommend it to anybody. Yet there are times when you do not want to cache a whole page for long periods, but to cache sections of it so that a page rebuild is not as costly in server time.

For example I had a site where they did not want caching on the homepage as a whole as they wanted to have the following features :

  • • Rotating background image provided by php
  • • Alternating advertising scripting for comparison of suppliers
  • • A loop that updates every 5 minutes, along with others that updated hourly

It was chosen that a “sub caching” (to use the customers preferred term) strategy would be best. Now you understand the scene of what we need to achieve, a cached homepage which could have items within it updated regularly, that did not use the page cache of W3TC.

To achieve all of what the client wanted, I needed to use the Transients API for WordPress

WordPress Transients API

The WordPress transients API is a simple set of three commands that work very much like Options API, but when a timer that you set runs out, the transient is automatically deleted. Inside a transient you can save any kind of data you like. It can be an array, object or just some text, it really does not matter what you store in there as WordPress takes care of it by serializing it as necessary.

What the Transients API also does is automatically save the transient in the quickest responding place on the server. This could be in the database, on the disk or in memcache depending on your servers configuration.

Here is the basic syntax for getting and setting a transient:

$value = get_transient( 'value' );
if ( false === $value ) {
        // if transient not set, do this!

        // create the data that needs to be saved.
        $value = 1;

        // save the newly created transient value
        // 60 seconds * 60 minutes * 24 hours = 1 day
        set_transient('value', $value, 60*60*24);

Notice how they work in tandem with each other. If there is nothing set in the transient called ‘value’ then the code knows to set a new ‘value’ from the code inside the if statement.

As you can see, all we do to set a transient is use set_transient() with the name of the transient, in this case ‘value’, with it’s $value being the data you want to save, and the last part being the calculation that would created the number of seconds that the transient should last for.

Using transients on a single loop inside WordPress

Unless you are an ultra busy site with 100’s of content creators, you can be pretty sure that the loop that shows your posts in your blog does not change that often. Be that days or only minutes, caching will speed up the loop, as the server will not have to ask any complex SQL queries, but a simple single request to get the pre-found data, a much quicker and less intensive thing to do.

$loop = get_transient( 'loop' );
if ( false === $loop ) {

       // Show the last 100 tweets from the custom post type tweets.
       $query = array('post_per_page' => 100,
                     'post_type' => 'tweets',
                     'post_status' => 'publish' ) ;

       $loop = new WP_Query($query);

        // transient set to last for 1 hour
        set_transient('loop', $loop, 60*60);
// do normal loop stuff
if ($loop->have_posts()) : while ($loop->have_posts()) : $loop->the_post();

// show content or whatever you like



The above query for the 100 tweets from the custom post type of tweets will now be saved inside the transient called ‘loop’

A Question of Data or Information

Using the above transient we are dealing with Data, or un-formatted information. This is all fine, and works just great, but you could take things a little further.

Instead of manipulating the data every time that the page is loaded, it can be better to add all of the HTML to the loop and then save it to the transient so that it is even quicker to load.

As shown in the below widget, all of the widget is built as a full HTML section before it is saved. This is my preferred method of implementation as there is less processing to do.

Caching Widgets with transients

Many widgets show content that hardly ever changes, for example categories on the site, or pages on the site. It’s only really things like latest comments that tend to change often, and even then it’s doubtful that would be more than once per minute. So even with the more active widgets there is space for caching, let alone the sedentary ones.

If you have a busy site for some reason, that could be thousands of hits inside that minute that take up unnecessary processing time to check again and again all of the widgets and their output.

Caching widgets is pretty easy, here is an example widget for displaying a table of events from a custom post type called ‘ak_events’.

class show_ak_events_Widget  extends WP_Widget {
    function show_ak_events_Widget() {
            /* Widget settings. */
            $widget_ops = array( 'classname' => 'ak-events', 'description' => 'Shows events in a table' );
            /* Widget control settings. */
            $control_ops = array( 'width' => 300, 'height' => 350, 'id_base' => 'ak-events' );
            /* Create the widget. */
            $this->WP_Widget( 'ak-events', 'Show Events', $widget_ops, $control_ops );

    function widget( $args, $instance ) {
            extract( $args );
            // get cache if it exists
            //  $widget_id comes from the widget $args->widget_id and is the widgets unique ID
            $output = get_transient('events'.$widget_id);

            // if no $output do stuff inside this if statement
            if ( $output === false ) {
                 // set the title variable
                 $title = apply_filters('widget_title', $instance['title'] );

                // standard opening of widget
                $output = $before_widget;

                // if a title exists add it to the top of the widget
                $output .= ( !empty( $title ) )? $before_title . $title . $after_title : "" ;

                // Create query arguments for WP_Query to use
                $widgetargs =  array( 'posts_per_page'=>'-1',

                // WP_Query sets up a loop query
                $query = new WP_Query( $widgetargs );

                // create the opening table and top row
                $output .= "<table><tr><th>Event Name</th><th>Information</th></tr>";

                // If the WP_Query has results send them through the loop
                if ($query->have_posts()) : while ($query->have_posts()) : $query->the_post();
                    $output .= "<tr><td>" . get_the_title() . "</td><td> " . get_the_excerpt() . " </td></tr>";

                // close the table
                $output .= "</table>";

                // close widget properly
                $output .= $after_widget;

                // save $output as a transient and set it to be 60 seconds * 5 = 5 minutes.
                set_transient( 'events'.$widget_id, $output, 60*5 );

            echo $output;


    function update( $new_instance, $old_instance ) {
            // save form data
            $instance = $old_instance;
            $instance['title'] = $new_instance['title'];

            // delete the transient so the new title setting is used
    return $instance;

    function form( $instance ) {
         $defaults = array(
        $instance = wp_parse_args( (array) $instance, $defaults ); ?>
            <label for="<?php echo $this->get_field_id( 'title' ); ?>">
                <?php _e('Title:','proving-ground'); ?>
            <input id="<?php echo $this->get_field_id( 'title' ); ?>"
                      name="<?php echo $this->get_field_name( 'title' ); ?>"
                      value="<?php echo $instance['title']; ?>" style="width:95%" />


As you can see, all of the loop and processing activity takes place inside the if statement that runs when the widget is called. The other thing to notice is that there is also delete_transient() used inside the update function to delete the transient every time you update the widget on the admin screen.

The bit that makes this work is really the ‘events’.$widget_id bit, as that assigns a unique transient name to each instance of the widget that is loaded. So the widget used in one sidebar is cached separately from the same instance of the same widget .

Using caching for theme options

Theme options are more and more common inside WordPress themes. They hold things like the settings for the theme which are applied every time the site it loaded. This is fine, but it is requested from the database each time, as that is the way that get_option() works.

If you use get_transient() instead you might not be going to the database to request it. It’s possible it’s coming from memory cache or disk cache, which are quicker to access.

Here is a simple little function I made to manage this for me, whenever I need to get_option() I use my_get_cache_option() to get the WordPress option I want. The following code goes in your functions.php

function my_get_cache_option($option_name = 'ThemeAdminOptions' ){
    // get wanted transient
    $value = get_transient( $option_name  );
    // check if it has any content
    if(false === $value){
        // if no content in the transient get new copy of wanted option
        $value = get_option( $option_name );
        // set new transient with a refresh of 1 day
        set_transient( $option_name, $value, 60*60*24 );
    // return the transient $value or newly created $value
    return $value;

Personally I setup default of ‘ThemeAdminOptions’ and use that for my theme options, but if I want to get a different transient or option I use it like this:

// get cached option
$options =  my_get_cache_option('WantedOptionName');
// do code with options array/object/string
// example code
echo $options['google_analytics_id'];

If you are going to use this solution, don’t forget to use delete_transient() within the process of saving the updates on your admin pages. Normally you would just update the options, but as well as that you need to delete the transient that exists as your other code will pull the old data from the transient for up to 24 hours.

// update the option as normal
update_option('WantedOptionName', $value );

// delete the transient that may be set


Using transients for timed events

One of the great benefits of the Transients API is the ability to setup very simple timed events that perform functions for you.

Say you wanted to get an RSS feed every minute from a stock trading company, it could be easily achieved by using the following function and call:


function setup_timed_event(){

    // get value if set;
    $value = get_transient('run_batch');

    // if not set run if statement;
    if($value === false){
            // run every time the timer runs out

            // set any value to $value;
            $value = 'done';

            // setup transient to last for 60 seconds;
            set_transient('run_batch', $value, 60);

function do_minute_batch(){
 // code to run in batch here that calls the RSS feed.

// use an action to call the timed event every time the init is called. 

It is not strictly true that it will run every 1 minute, as if the server performs no activity of any description for more than a minute, it will not run, but will run on the next activity whatever that is.

Other activities that can be cached with transients

The list is endless of things that you can make quicker on a website by using the transients API. Anywhere you have to process data, you can use it. If you write a twitter plugin, you could use it to save the Twitter feed and only update when it is deleted. Or maybe you have an Amazon store that you want to setup with caching for small periods, this is perfect and simple to use. In fact anywhere you need to get data from an external source, cache it with the transients API.

There is nothing to stop you taking caching to the nth degree with multi loops with differing cache timers based on importance of content, or make sure that every single widget you have caches for the right content lifetime. In fact there is nothing to stop you giving the caching timeout control to the widget administrator by having a field that is the number of seconds or the calculation that sets the time period of the widgets set_transient(). It really is endless where you can go with this.

What about things that echo to the screen?

In WordPress there are quite a few things that automatically echo to the screen, such as the_content(). Sometimes you want to use these items rather than the get equivalent get command, i.e. get_the_content(), as the formatting is already good without the need for extra manipulation.

Or maybe a plugin developer has added an action or filter to the command, for example the_content() could also have an action or filter added to it that appends share links and icons to the end of the content.

Both of these examples call for a slightly different way of saving the data in to the transient. We now have to use an ob_start(),ob_get_contents(), ob_end_clean() combination to save the data we are echoing to the screen and then save that as a variable as the transient

$loop_output = get_transient( 'loopOutput' );
if ( false === $loop_output ) {

       // Show the last 100 published posts.
       $query = array('post_per_page' => 100,
                      'post_status' => 'publish' ) ;

       // run the query
       $loop = new WP_Query($query);

       // start the output buffer to save contents of loop

            // do normal loop stuff
            if ($loop->have_posts()) : while ($loop->have_posts()) : $loop->the_post();

                // show content or whatever you like
                ?><h1><?php the_title() ?></h1><?php


            // save the output buffer contents in a variable
            $loop_output = ob_get_contents();

       // clean the buffer as we will be using the variable from now on

       // transient set to last for 1 hour
       set_transient('loopOutput', $loop_output, 60*60);

// output the new created loop if loop content does not exist. 
echo $loop_output;


Rounding up

Using the transients API can significantly increase the speed of your WordPress plugin, theme or widget. It really is worth considering making this standard practice when creating for WordPress in future.


Thanks loads to Erik for pointing out that you have to have WP_CACHE enabled for any of this to work. As the ChiefAlchemist rightly says, that would cause many a grey hair and long night not to know.

To set WP_CACHE, you need to edit the wp-config.php in the root of your webserver (or one directory above if you are security minded).

define('WP_CACHE', true);
/* Absolute path to the WordPress directory. */

It goes at the end just above the line “Absolute path to the WordPress directory”

Caching Shortcodes

Also by request, shortcodes. The only issue with caching shortcodes is that you need to consider where and how it will appear. here are a few different examples of where you might find yourself using shortcodes.

  • Shortcode without any settings, but relates to the post. i.e. share links to social networks
  • Shortcode with settings. i.e. show a gallery by ID
  • Shortcode with no settings or relationships. i.e. show current number of facebook followers
  • A mix of all of the above
// shortcode without any settings but relates to the post.  can only be used inside the loop.
// shortcode usage
// [share] 
function sharelinks( $atts ){

$links = get_transient('share_links' . get_the_ID() );
if($links === false){

// add the links you want
$links = "<a href='" . urlencode( get_permalink() ) . "&t=" . get_the_title(). "'></a>";

// save the count for 30 days
set_transient('share_links'  . get_the_ID() , $links, 60*60*24*30 );
// return the response. **do not echo**
return $links;
// set shortcode 
add_shortcode( 'share', 'sharelinks' );

shortcode example with settings

// shortcode usage
// [my_gallery id="1" height="300" width="200"] 

function shortcode_gallery( $atts ){
// get the values from inside the shortcode and make them variables
// the id, height and width have the default settings inside the array if not set
// in the shortcode. 
extract( shortcode_atts( array(
		'id' => '1',
		'height' => '100',
                'width' => '60',
	), $atts ) );

$gallery = get_transient('gallery' . $id . $height . $width );
if($gallery === false){
//  Do the code that creates your gallery and return the output to a variable called Gallery
$gallery = get_my_gallery($id, $height, $width);

// save the count for 30 days
set_transient('gallery' . $id . $height . $width, $gallery, 60*60*24*30 );
// return the response. **do not echo**
return $gallery;
add_shortcode( 'my_gallery', 'shortcode_gallery' );

shortcode to show facebook follow count

// shortcode usage
// [fans]
function facebook_fans( $atts ){
$fans = get_transient('facebook_fans');
if($fans === false){
// put in your own facebook ID
$page_id = "YOUR PAGE-ID";
// get the XML from facebook with the response
$xml = @simplexml_load_file("  method=facebook.fql.query&query=SELECT%20fan_count%20FROM%20page%20WHERE%20page_id=".$page_id."") or die ("a lot");
// get the fan count
$fans = $xml->page->fan_count;
// save the count for 1 day
set_transient( 'facebook_fans', $fans, 60*60*24 );
// return the response. **do not echo**
return $fans;
// set shortcode 
add_shortcode( 'fans', 'facebook_fans' );

The only trick to making this work really is having a variable that you can add to a word stem. As with the share links I used the post id to set it seperate, with the gallery I used the id, height and width as this will give full caching no matter how many different ways the shortcode is used.

More from the Website Speed series…

Website Speed Part 1: Write More Efficient CSS →
Website Speed Part 2: Working With and Optimizing Images for the Web →

(13 Posts)

Andy Killen has been working exclusively with the web since 1994 when he was Intel's internet engineer for Europe. Today he runs his own company as well as is the CTO of Speckyboy and was also part of the Adobe Fireworks CS6 beta test team. When Andy is not busy making websites, themes and plugins or giving training on website performance, wordpress or node.js, he can be found enjoying life in Amsterdam.


  • One important thing to note is that transient still is valid when WP_CACHE is not set (or set to false). Please consider to check the value of WP_CACHE when implementing transient.

  • andykillen

    You are absolutely right.  I might edit the article to add that.  very valid. 

  • andykillen

    Michael, nice site, loads quickly. :)

    I took the liberty of looking at your sites configuration and there are a few areas where you could improve the speed more.
    1) either combine by hand or get W3TC to combine your JS files into one, and look at what can be loaded at the bottom
    2) miniify your js and CSS
    3) 3 combine css files into 1, either by hand or with W3TC
    4) use sprites for your images where possible.
    5) crush your images, I averaged 30% saving on the ones I tried.
    6) relative paths for css based images and fonts as your not using a CDN
    7) you could also strip a lot of CSS out as it is just setting the same values many times, or just longer to write.

    border-right:1px solid #dbdbdb;
    border-left:1px solid #dbdbdb; 
    border-top:1px solid #dbdbdb;

    could be

    border:1px solid #dbdbdb;
    border-width:1px 1px 0 1px;
    If you have a check through part 1 and 2 of the website speed series, you will find much help on these subjects.As your site is really nice, I just want to highlight that there are a few things you can do tomorrow to make it even faster. :)All the bestAndy

  • transients is something I would say major command that solve many complicated problems in perfect way. great use of transients commands thanks!

  • Thanks again Andy.

    For me this article comes at a really good time. I’ve got a couple WordPress plugins/widgets I’ve been working on. You already mentioned widgets but these all use shortcodes too. The idea of WP reinventing the wheel every time has been bothering me. I’ll have to wrap my head around this as it might apply to shortcodes. In the meantime, if you can toss in your 2 cents – or ideally 5 cents – that would be great. I’ve been fried lately and any pointing in the right direction would be appreciated.

    One minor suggestion, I presume this WP feature is more or less the WP-ism of some PHP code. For the non-WP’er perhaps you could have talked about the PHP implementation of this concept?

    p.s. If you’re into jQuery and Google Analytics Event Tracking I have something I’d like to have you alpha for me. Thanks.

  • Sounds important. Yes, please update so the rest of us can avoid some grey hairs.

  • Dan

    I use WP Super Cache instead.

  • andykillen

    I’ve added a bit on shortcodes, I hope it’s what your looking for. And also updated the WP_CACHE details in the post to make it clear.

    I’ll contact you off-line about your jQuery/Google thingy, sounds very interesting. 

    Oh and I will be doing 2 or 3 posts on programming specific speed up methods. I’m making a bunch of test data to prove it all, so it will take a little while. So it sounds like a good plan to do a selection of techniques to employ rather than just coding best practice.

  • andykillen

    Hey Dan, I’ve used that one as well, but find I prefer to use W3TC. It has an arm full of more options to help with other parts of the page than just html publishing. I like the CSS and JS parts as well as the object/database caching it offers as I can get finer grade control on very active sites.

    Either way, Super Cache or W3TC, they both can take advantage of the caching we are doing in the above code. :-)

  • 1) great. thanks. I’ll check it out. I’m hoping there’s a way to manually force the transients cache to clear. I understand they’re not for every occasion but at the same time I don’t want to not use them because of a couple rare use cases, and there’s a “use as necessary” manual work around. I’ll come back with a real example if I can’t sort it out. I have one in mind but I won’t bother with typing it out.

    2) cool. thx.

    3) that’s gonna help me too. i’ve got a WP plugin I wrote when I was less savvy and it turns out it’s a bit of resource hog. i’d like to tune it up a bit. But given i’m mostly a hunt &peck, just get it to work, weekend warrior kinda PHP dev’er, i’m open to all the help I can get. i love details. your stuff is always that extra magic that more people really need to know and practice. thanks for sharing.

  • andykillen

    you can just use the delete_transient(‘TransientName’);  command to delete it from cache.  Or if you have W3TC installed and need to clean all caches in one go, just use that with the clear all caches button.  Kinda vital for testing and debug if you don’t want to wait for the caches to run out before you can see what’s going on.  

    The main places where you would not want to use them is where you have too many differences.  For example caching the menu may seem like a good idea at first as all the code is there for the complete menu, but the classes like current-page would be wrong on every page except the first page that was cached on.

    Another place where it is more difficult to use them is when dealing with content that is different if you are logged in or not, inside the IF statement does not work, so you need to have two separate transients, one for each state, each with it’s own separate IF statement with relevant code. So you can end up with a near duplication of code.

    thanks for the thumbs up :)


  • andykillen

    oh the share links example really in truth I expect would take longer to get the transient rather than just doing the code, it was just there to give an example.  I don’t think I would actually setup this code in the real world.

  • Thx. I’ll look at that.

    My specific example is this: I’m consuming an RSS feed from Flickr for a Flickr set. I want to display that with a shortcode via a jQuery gallery/slider. If I can cache that, it just makes sense.However, if a photo is added to the set then I want to flush that transients so that it picks up the new photo sooner rather than later.

    As I said, I admit, it’s a rare use case. None the less, I hate being half-assed and not catching things where there should be something. Even if that something is the 20 side of 80 / 20, I don’t like looking like a chump.

    Seriously, this is great stuff. The devil is in the details. When you know stuff like this you can be more creative with more innovative solutions. Your depth and thoroughness is inspiring.

  • andykillen

    I’d go for a small cache time of 5 mins (60*5) and know that it won’t be updated any more often than that. In fact do you know what the refresh time that flickr takes on it’s RSS feeds?  no need to be any more frequent than

    here’s a quick quote on the flickr forum about RSS
    “As I said above, RSS updating on Flickr is not instant. It can take anywhere from a few minutes to a few hours. This is normal.”

  • Flickr RSS update rate? Good question. I guess I’m about to find out :) I had to trouble shoot an issue with Galleria and WP first.

    I want to send you the alpha of jQuery plugin I mentioned. How can I get that to you?

    Thanks again. When I grow up I wanna be just like you :)

  • Jack

    Your timed event example makes me think I can create a date/time widget
    that will refresh every minute on an internet functional TV that is set to display a
    WP site designed as a Digital Sign. I keep thinking the transient feature is only going to work if the client browser initiates some activity through a clicking event. Is this the case? Otherwise the transient feature implies that WP is running some sort of daemon on the web server.

  • Arelthia Phillips

    I will have to give Transient a try to help improve the speed on my site. Thanks for the series

  • Andy

    any server activity causes the transient to be updated due to it being applied to the init hook

  • Andy from Workshopshed

    I believe you can also use a external CRON job to poll http://yoursite/wp-cron.php if you are worried that there might not be enough users hitting the site.

  • If you have a blog with 5k per day visits , then should You use the caching or not?

  • wow thank you. I wasn’t aware of transients.. on content needing periodic refreshing I always just stored two values in `get_option()`: one the value, another a expire time… then manually compared times..