Did you know that WordPress has a built-in caching system called transients? They are easy to use and extremely effective when used correctly. In this article I will demonstrate how we used the WP Transient API functions and made our own front page load on average over 300% faster

Caching can sometimes be tricky because selecting good expiry time settings and doing cache invalidation requires a profound understanding of the whole website by the developer. However, if you are not doing any at all and don’t have any transient functions in use on your site, then you are surely missing out on some serious WordPress speed improvements.

The WordPress transient API: get_, set_ or delete_transient()

The WordPress transients API is extremely simple. There are only three functions: a setter,  a getter and a deleter. With the setter you save something in the cache, and with a getter you fetch it. The setter takes tree parameters: the name of the cache (referred as ‘cache key’), the value/contents to store in the cache, and the expiry time in seconds. The getter is even simpler: you only give it the cache key as a parameter and it will return the value if found, or false if nothing was found. The deleter is used to invalidate keys, meaning they are deleted even before they would otherwise expire. Below is the classic example from the WordPress documentation showing a transient used to store a database query result, so that the potentially heavy database query does not need to execute all the time:

// Check for transient. If none, then execute WP_Query
if ( false === ( $featured = get_transient( 'foo_featured_posts' ) ) ) {
      $featured = new WP_Query(
		'category' => 'featured',
		'posts_per_page' => 5

	// Put the results in a transient. Expire after 12 hours.
	set_transient( 'foo_featured_posts', $featured, 12 * HOUR_IN_SECONDS );
} ?>

What to store in a transient?

Anything that is heavy to compute and where the result is valid for at least some time is worthwhile to store in a cache.

Case: front page

There are two preferred ways to analyze what is going on in the PHP code when a WordPress page load executes: XDebug or Tideways. On production sites Tideways is the only sensible option. A manually triggered trace on our front page yielded the waterfall graph below, which revealed where in the code most of the execution time was spent. The culprit was the template files that fetched a lot of custom post types from the database.

Below is the contents of the initial version of our frontpage.php WordPress theme template file. It was basically just loading a bunch of template partials, which themselves contained a bit of boilerplate code and some simple loops over custom post types. As the Tideways profiling revealed, producing those partials was fairly heavy on the database. As the front page is rather static, and basically only the section that lists our latest blogs changes, this was a good candidate for caching.

get_template_part( 'partials/fp', 'hero' );
get_template_part( 'partials/fp', 'features' );
get_template_part( 'partials/fp', 'segment-section' );
get_template_part( 'partials/fp', 'products' );
get_template_part( 'partials/fp', 'customers' );
get_template_part( 'partials/fp', 'blog-section' );
get_template_part( 'partials/fp', 'cta-section' );
get_template_part( 'partials/tech', 'logos-section' );

Our approach was to make a wrapper function, that would fetch the template from the transient cache if found, and only if not found it would actually render the partial, store the output using the PHP output buffer and then echo out the partial’s contents that was either just generated or fetched from the cache. The inline code comments explain each step in detail.

 * Wrapper for get_template_part to get the ready-rendered
 * template from the WP Transient cache is exists.
 * Note! This should be only used for static pages that always
 * have the same contents, or at least can be so for an hour
 * (which is the current cache expiry time).
function templateCache( $template_path, $template_name ) {
  if ( cntrst()->helpers->is_mobile() ) {
    $cache_key = 'seravo-partials-'. $template_name .'_mobile';
  } else {
    $cache_key = 'seravo-partials-'. $template_name;
  if ( ! $output = get_transient( $cache_key ) ) {
    get_template_part( $template_path, $template_name );
    $output = ob_get_clean();
    set_transient( $cache_key, $output, HOUR_IN_SECONDS );
  echo $output;
get_template_part( 'partials/fp', 'hero' );
templateCache( 'partials/fp', 'features' );
templateCache( 'partials/fp', 'segment-section' );
templateCache( 'partials/fp', 'products' );
templateCache( 'partials/fp', 'customers' );
templateCache( 'partials/fp', 'blog-section' );
get_template_part( 'partials/fp', 'cta-section' );
get_template_part( 'partials/tech', 'logos-section' );

The result: down to 143 ms from 450 ms

A new manually triggered trace on Tideways validates that the optimization worked as intended. The segment-section and customers partials that were clearly visible in the previous trace have basically disappeared but the hero and cta-section we did not wrap in our cache wrapper function are visible. 

Note that in these two pictures the scale is radically different. In the first Tideways waterfall view the scale is 0–450 milliseconds, while in the second one the span between left and right sides of the graph is only 143 milliseconds.

Running our own wp-speed-test tool from inside the server itself also verifies that the PHP of the front page is generated much faster than before:

seravocom@seravocom:~$ wp-speed-test 
Testing speed URL

For an explanation of the different times, please see docs at

URL                  TOTAL NAMELOOKUP CONNECT APPCONNECT PRETRANSFER STARTTRANSFER    = AVG   0.405      0.060   0.061      0.064       0.064         0.403    0.405   0.380      0.000   0.000      0.000       0.000         0.379    0.392   0.313      0.000   0.000      0.000       0.000         0.312    0.366   0.330      0.000   0.000      0.000       0.000         0.328    0.357   0.340      0.000   0.000      0.000       0.000         0.339    0.353   0.331      0.000   0.000      0.000       0.000         0.330    0.350

seravocom@seravocom:~$ wp-speed-test 
Testing speed URL

For an explanation of the different times, please see docs at

URL                  TOTAL NAMELOOKUP CONNECT APPCONNECT PRETRANSFER STARTTRANSFER    = AVG   0.098      0.004   0.004      0.008       0.008         0.096    0.098   0.092      0.000   0.000      0.000       0.000         0.090    0.095   0.092      0.000   0.000      0.000       0.000         0.091    0.094   0.086      0.000   0.000      0.000       0.000         0.085    0.092   0.089      0.000   0.000      0.000       0.000         0.088    0.091   0.090      0.000   0.000      0.000       0.000         0.089    0.091

Thanks to Tideways, we don’t have to rely on a few manually made measurements before and after, as we can also look at the long-term multi-sample history that Tideways keeps. In this case the result was indeed drastic:

Tideways history view. Note the WP transient usage that started around 16:00.

Use transients, and a server that has Redis and object cache enabled

We warmly encourage all developers to start using the WordPress transients feature to optimize their website for improved speed. To get the most out of it you should be running a server environment where the transients are stored in RAM memory. At Seravo all of our customers have the Redis server enabled by default and the benefit of using transients is at its highest.

Do you want to receive articles like these directly to your inbox? Subscribe to our developer newsletter and stay ahead of the competition with the latest and greatest WordPress development tricks, tools and techniques.

Leave a comment

Read more

In category: Development

PHP 8.2 Soon Available

PHP 8.2 becomes available at Seravo for developer testing. However, we recommend using PHP8 on your live WordPress site – for now!

February 3, 2023

Upcoming PHP Updates at Seravo

Since 2022 PHP 8.0 has been the default PHP version at Seravo, and is automatically enabled on all new sites. PHP 8.1 is also available, and PHP 8.2 will be soon available for developers!

January 25, 2023

PHP 8.0 Enabled by Default

PHP 8.0 is the default version at Seravo’s premium WordPress hosting and upkeep. PHP 8.1 also available!

January 8, 2022

When the Internet Breaks – Make Sure Your Website Works

On Monday, October 4th 2021, something quite exceptional happened that did not go unnoticed by Seravo’s customers. Facebook and other […]

October 9, 2021

Webinar: Search Function and How to Customize It

Seravo’s first webinar of 2021 on January 14th: How to use and customize the WordPress search function?

December 30, 2020

What’s new in PHP 8?

On November 26th 2020, there will be a new version of PHP released. This new release has a number of new features, as well as implementing some non-backward compatible changes. Seravo has already started testing and deploying the release candidates of PHP 8.0 to our servers.

November 19, 2020