Coming up in WP Super Cache 1.5.0

The next version of WP Super Cache will be one with some big changes! There are many small bug fixes and improvements but the one I’m most excited about is moving the legacy cache files into the supercache directory.

The legacy cache files were the files created by the old WP-Cache plugin upon which this plugin is based. They’re really useful as they store the headers sent from the server as well as the page contents. If you’re serving pages that aren’t regular html, such as JSON or XML you don’t want to tell the browser they’re text/html documents. This caching method is also used for anyone who is logged into your site, or left a comment.
There is a problem however. They’re stored in one directory. If you have many thousands of visitors interacting with your site you may end up with a directory containing thousands of files. The names of the cache files are a hash of the URL, gzip support and browser cookies so one file can match one user, or one file can be used by thousands of anonymous users. In the event that someone left a comment on a popular post the plugin has to search through all those files looking for the pages cached for other users who were also looking at that page. On a busy server that can cause problems.

So, in #177 I added code that moves the legacy cache files into the supercache directory. That means the files are stored in directories that reflect the URL of the page that was served which makes it very easy to delete the cached files belonging to that page as they’re all in the same directory!

The new code will look in the old location for legacy files first as some sites will have a large collection of cached files, but any new cache files will be created in the supercache directory.

Ian Dunn submitted code to cache the REST API. It’s not yet complete but we’ll be able to build on the changes to the legacy cache to make caching the API more efficient than it would have been before.

I really need people to help test this. The latest code is running on this site so I’m very confident in how well it works but just because it works on my odd little server doesn’t mean it will work right everywhere. If you want to give it a spin, visit the plugin Github repository and click on the “Clone or download” button. If you don’t know how to clone a Git respository just grab the zip file and install it on your server, overwriting the files in the plugins/wp-super-cache/ directory. If the changes to where cache files go doesn’t interest you, some of the changes in this list might:

WP Super Cache 1.4.9

There’s a new release of WP Super Cache out and it’s a security release to fix XSS problems in the settings pages. Those pages are only accessible by admin users so an anonymous visitor to your site can’t come along and enable it to steal your login cookies but along with those fixes come many bug fixes so it’s worth upgrading if you’re using an old version.

From the Changelog:

  • Fixed bug when not running sem_remove after sem_release. See
  • Fixed a PHP error impacting PHP 7.1.
  • Fixed a bug where we cached PUT and DELETE requests. We’re treating them like POST requests now.
  • Delete supercache cache files, even when supercache is disabled, because mod_rewrite rules might still be active.
  • Updated the settings page, moving things around. #173
  • Make file locking less attractive on the settings page and fixed the WPSC_DISABLE_LOCKING constant so it really disables file locking even if the user has enabled it already.
  • Added a WPSC_REMOVE_SEMAPHORE constant that must be defined if sem_remove() is to be used as it may cause problems. #174
  • Added a “wpsc_delete_related_pages_on_edit” filter that on returning 0 will disable deletion of pages outside of page being edited. #175
  • Fixed plugin deleting all cached pages when a site had a static homepage. #175
  • Make sure $cache_path has a trailing slash #177
  • Remove flush() #127 but also check if headers are empty and flush and get headers again. #179
  • Add fix for customizer #161 and don’t cache PUT AND DELETE requests #178
  • Check for superglobals before using them. #131

You can click through to each of the Github pull requests above to see discussion around each bug fix.

If you’re hosting many sites that use WP Super Cache and you’re seeing issues with semaphores it may mean that your users are using file locking. It’s really not needed and in #174 there’s a fix that went into this release. You can disable file locking completely by setting the constant “WPSC_DISABLE_LOCKING” in a global configuration file. The file locking simply slowed down how fast cache files were created and is a hold-over from WP Cache when that plugin used to write directly to the cache files. This plugin writes to temporary files before moving to the final cache files so that locking isn’t really needed, but some sites still use it which is why it’s still around.

I’ve already been working on the next release with efforts to move the legacy cache files into the supercache directories. This will make it easier to maintain them and improve performance. We really need to find a better name for this caching method however. It caches everything – page contents and http headers so it’s quite useful!
If you’re going to test that PR, try #176 too. The plugin only deletes index.html type files right now but this chunk of code cleans up various for loops in the plugin and also deletes any file in the named directory. There are some restrictions on it so it won’t delete anything outside the cache directory.

Thanks to everyone who contributed to this release!

WP Super Cache 1.4.5

WP Super Cache is a fast caching plugin for WordPress. It will help your site run faster and serve more traffic.

This is a security and bugfix release.

  • Some servers display a directory index when no index.html is found in a directory. That may reveal the filenames of cache files.
  • There were issues in the settings page that might allow an attacker to browse or delete files named index.html.
  • PHP Object Injection could occur if an attacker managed to inject malicious code into the legacy cache meta files.

When you upgrade, your “legacy cache” files for logged in users will be deleted. This may have an impact on your site:

  • If your site is slow at generating new pages.
  • If you have many known users (logged in users or people who comment).

Your site will suddenly have to generate new cache files for all visiting known users.

Relying on caching like this is not recommended for these types of users as it’s very inefficient. Each user has a separate cache file that must be checked whenever the plugin does administration work like cleaning up stale cache files.

If most of your traffic is anonymous users who don’t comment you don’t need to worry about this.

Directory Listings

If a server is configured to show directory listings it will show files and directories in the cache directory to visitors who access those directories directly through their browser. This might reveal private posts, and in the case where legacy caching is enabled for known users the login cookie was stored in “.meta” files that could be downloaded.


Files named “index.html” were added to the main cache directories to stop remote users viewing the contents of the cache directories. Unfortunately it’s not possible to add empty index.html files to the supercache directories because those files could be served by accident to legitimate visitors of the site. However, the plugin will also add a directive that disables directory listings to the file cache/.htaccess. You can now also change the location of the cache directory on the Advanced Settings page of the plugin. If you can’t disable directory indexing on your server and you have private posts you should change this location and use PHP mode to serve cache files.


If a directory index is found in the cache directory it will show a warning like this to administrators:

index.html warnings

Clicking the logout link will log everyone out, except the user who clicks it, but it guarantees that the login cookies are updated, just in case someone has copied the cookie from an old meta file.

Directory Traversal and File Deletion

User input in the settings page wasn’t properly sanitised. The code that sanitised directory paths when deleting cache files wasn’t secure and might allow an attacker to view or delete files named index.html. Deletes are protected by a nonce, limiting the useful lifetime of the URL however.

PHP Object Injection

The plugin used serialize and unserialize to store data in “legacy cache” meta files. This might be used to perform a PHP object injection attack. Serialised data is now stored as JSON data.

The format of legacy cached files has changed. The files in the meta directory no longer have a .meta extension. They are .php files now and each file has a “die()” command to stop anyone loading them.
The data stored in those files is now stored as JSON serialised data. The login cookie is an MD5 hash now as well.
When you upgrade the plugin your existing legacy cache files will be deleted and regenerated as visitors use your site.

Apart from those security fixes there have been a number of enhancements and bugfixes:

  • Disabling the plugin no longer deletes the configuration file. Uninstalling will do that however.
  • Enhancement: Only preload public post types. Props webaware.
  • It’s now possible to deactivate the plugin without visiting the settings page.
  • Fixed the cache rebuild system. Rebuild files were deleted immediately but now survive up to 10 seconds longer than the request that generate them.
  • Minor optimisations: prune_super_cache() exits immediately if the file doesn’t exist.
  • The output of wp_cache_get_cookies_values() is now cached per visit.
  • Added PHP pid to the debug log to aid debugging.
  • Various small bug fixes.
  • Fixed reset of expiry time and GC settings when updating advanced settings.
  • Removed CacheMeta class to avoid APC errors. It’s not used any more.
  • Fixed reset of advanced settings when using “easy” settings page.

This release wouldn’t be possible without the help of Brandon Kraft, Dane Odekirk, Ben Bidner, Jouko Pynnönen and Scrutinizer. Thank you all!

WP Super Cache 1.4.4

WP Super Cache is a full page caching plugin for WordPress. It creates static pages that are served quickly by the web server.

Light Trails in Blarney

Over the weekend there was a flurry of excitement as not one, but two releases of the plugin were made in quick succession. The second to fix a bug introduced in the first. I’m very sorry about that.

WP Super Cache 1.4.3 fixed a security bug in the cache file listing section of the plugin settings page. A carefully crafted query by a third party would cause an XSS in the file listing. While serious, the owner of the blog cannot be tricked into loading the file listing page by way of an image or public link as a nonce is required to load it. The attacker would have to ask the blog owner to visit the cache listing page manually. Thanks to Marc Montpas from Sucuri for reporting that.

WP Super Cache 1.4.4 fixed a bug introduced in the previous version where queries with GET parameters caused a fatal error. Thanks to webaware on the forums for reporting that and including a patch to fix it.

If you’re using an older version of WP Super Cache you’re encourage to upgrade to 1.4.4 as soon as you can.

WP Super Cache 1.4

WP Super Cache version 1.4 is out now. This release finally removes the mfunc, mclude and dynamic-cached-content tags as I warned about three months ago.

If your site uses that dynamic cached content functionality do not upgrade yet. There is a replacement dynamic cached content system but it’s not compatible so you’ll need to update your themes and helper plugins. It’s not difficult but there’s a lot to take in. I hope the example plugin and explanation in that post gets you most of the way there.

If you don’t use mfunc and it’s friends then you should upgrade immediately and it should be painless.

This release also has a few bug fixes and other features. It will now try to repair broken installs after a site migrates. It will update the wp-config.php and rebuild wp-content/advanced-cache.php. It will also delete tags and category cache files when a post publish status changes.

The shiny new dynamic content in WP Super Cache

Edit: There’s a small bug in WP Super Cache 1.4.2 in the file dynamic-cache-test.php. Grab the development version instead. The documentation in the filehas been updated too. Copy the new file into wp-content/plugins/wp-super-cache/plugins/.

With the next release of WP Super Cache in a day or two the long awaited move away from mfunc, mclude and friends will be complete.

This means that if you have been using mfunc, mclude or dynamic-cached-content the dynamic portions of your sites will go blank if you upgrade WP Super Cache without updating that dynamic code. This may seem complicated but there’s an example script included and detailed explanations below. A lot of effort was made to make this backwards compatible but unfortunately it wasn’t possible.

In their place is a new cacheaction filter called wpsc_cachedata and it’s sidekick wpsc_cachedata_safety. In the future when a site owner using WP Super Cache wants to make part of their website dynamic they will use those filters to modify pre-defined text strings and replace them with the data they want displayed to the end user. There’s an example script ready to be ripped apart to help you figure all this out.

There are two ways of using this:

  1. The dynamic content is the return value of a simple process, be it date() or any of the numerous get_*() functions in WordPress. That data can simply be slotted in place of the pre-defined text mentioned above.
  2. The more difficult bit comes when you need to use an output buffer to collect the data for display. Due to a limitation in PHP it’s impossible to run an output buffer in the callback function of another output buffer, which is when the wpsc_cachedata filter runs. We need to collect that data before the callback function executes.

The first way above is easy. Simply add a text string of random characters to your theme where you want the dynamic content to appear, then hook a function on to the wpsc_cachedata filter to str_replace() it with your dynamic content. These functions in this script do that:

  • dynamic_cache_test_filter()
  • dynamic_cache_test_template_tag()
  • dynamic_cache_test_init()

You’ll have to hook on to the wpsc_cachedata_safety action and return the numeral 1 to actually run the wpsc_cachedata filter. This is a fail safe used by the plugin to make sure things are ok when the filter runs.

Unfortunately if you want to use an output buffer it’s a lot harder. As stated above an output buffer can’t run in the callback function of another output buffer. This means you have to generate your dynamic content earlier in the PHP process.
This could be as easy as calling your dynamic content function (dynamic_output_buffer_test() in the example script) from the wp_footer action, or calling it from any action before shutdown, whichever is appropriate. You’ll also need to add a template tag of your own choosing to your theme.
Your dynamic content function will run just fine for cached pages, so after the new page has run it store the output in a constant or global variable that the same function can look for when the wpsc_cacheaction runs it. If it finds that information, it can do the search and replace of your template tag without running the output buffer again.
Some pages won’t run the dynamic content function however. This includes feeds and sitemaps. Those pages will generate a PHP error because the output buffer will try to run in the callback!

“PHP Fatal error: ob_start(): Cannot use output buffering in output buffering display handlers in…”

To stop that happening you must check that there’s text to shove in the cached page. That’s what happens in the function dynamic_output_buffer_test_safety() in the example script. It fires on the wpsc_cachedata_safety filter and returns the numeral 1 if successful.

Anatomy of an Output Buffer

I’m going to try and explain the output buffer functions in more detail here. They’re only example functions and if you spot a bug or can suggest improvements please do!


This is the string you add to your theme where your dynamic content will appear. The plugin will replace this string with the code generated by your dynamic content function.

function dynamic_output_buffer_test( &$cachedata = 0 ) {
	if ( defined( 'DYNAMIC_OB_TEXT' ) )
		return str_replace( DYNAMIC_OUTPUT_BUFFER_TAG, DYNAMIC_OB_TEXT, $cachedata );
	// call the sidebar function, do something dynamic
	echo "<p>This is a test. The current time on the server is: " . date( 'H:i:s' ) . "</p>";
	$text = ob_get_contents();

	if ( $cachedata === 0 ) // called directly from the theme so store the output
		define( 'DYNAMIC_OB_TEXT', $text );
	else // called via the wpsc_cachedata filter. We only get here in cached pages in wp-cache-phase1.php
		return str_replace( DYNAMIC_OUTPUT_BUFFER_TAG, $text, $cachedata );

add_cacheaction( 'wpsc_cachedata', 'dynamic_output_buffer_test' );
  • 1. dynamic_output_buffer_test() is the function that will generate the dynamic content inserted into the cached page.
  • 2-3. DYNAMIC_OB_TEXT stores the previous output of this function. If it’s set that means we’re in the wpsc_cachedata filter and should insert it into the cached page and return it.
  • 4-8. This creates the output buffer, echoes a string with the current time and copies the buffer into a variable called $text before closing the output buffer.
  • 10-13. This is how we decide to return data. If $cachedata is 0 that means this function was called from the theme so we should define the constant DYNAMIC_OB_TEXT for later use. Otherwise, we must be dealing with an already cached page so insert the dynamic content into the page and return it.
  • 16. Add the dynamic_output_buffer_test() function to the wpsc_cachedata action. “add_cacheaction” is used as it loads before the regular WordPress action code loads.
function dynamic_output_buffer_test_safety( $safety ) {
	if ( defined( 'DYNAMIC_OB_TEXT' ) )
		return 1; // ready to replace tag with dynamic content.
		return 0; // tag cannot be replaced.
add_cacheaction( 'wpsc_cachedata_safety', 'dynamic_output_buffer_test_safety' );
  • 1-6. dynamic_output_buffer_test_safety() is a function that checks if dynamic content was generated correctly. If that constant is not defined the output buffer will run within the callback function of the main WP Super Cache output buffer and generate a PHP error.
  • 7. Add the wpsc_cachedata_safety() function to the dynamic_output_buffer_test_safety action.


As an example, if I wanted to display my dynamic content in the sidebar of my blog I would load sidebar.php in my theme’s directory and add the following code.

if ( function_exists( 'dynamic_output_buffer_test' ) )

I had previously edited the example script, uncommented it and changed the appropriate tag:


Final Note and Download Link

Please grab the development version of the plugin and try it on a staging server before you put it live. Feedback would be appreciated!

Warning! Keep the tags you use secret. You don’t want someone leaving a comment on your blog with that string! Do not use the same function names or constant names as in this post or example script. They’re in this very public post. Someone is bound to use them and cause you problems when you install their plugin.

Finally, barring any last minute major bugs this version of WP Super Cache will be released on Wednesday. Be careful upgrading. Pass the word around if you know someone is using mfunc as their site will stop working!

mfunc in WP Super Cache 1.4 and beyond

WP Super Cache is a full page caching plugin for WordPress. It creates static pages that are served quickly by the web server. Sometimes however, users still want parts of their pages to remain dynamic and be non static. That’s where mfunc, mclude and dynamic-cached-content came in.


Unfortunately it was reported recently that remote visitors to sites using the plugin could execute any code they like by simply leaving a comment containing the right mfunc code. These functions are now disabled by default, and a filter removes harmful code from comments but if enabled they pose a security risk. I considered adding a security code to the mfunc tag but unfortunately the best way of dealing with this problem is to replace it completely with something different. The next release of the plugin will do away with mfunc, mclude and dynamic-cached-content entirely.

The new dynamic cache system

The development version of WP Super Cache has already been updated with a new filter based system. It uses a cacheaction filter called “wpsc_cachedata”. This filter runs when a page is first cached and also when a cached page is subsequently served. It also runs when caching is disabled for known users, something that has always been broken when using mfunc.

Almost all the data that is displayed on your website will run through the cacheaction filter “wpsc_cachedata”. When a page is first cached, the data that is shown to the first visitor of that page goes through that filter. The second visitor gets a cached page and that page too goes through the filter. What this allows us to do is define a template tag (or more than one) that a function hooked on that filter can search for in the filtered data. It can replace that tag with some other text, usually derived from code that has to run on each request. The visitor is then shown the page with the replaced tag.

Example code

The readme.txt hasn’t been updated yet but an example plugin, dynamic-cache-test.php is included in WP Super Cache. It’s fairly simple but it’s documented so it should be easy enough to follow. A template tag is inserted at the bottom of the page using the wp_footer action, and a filter then replaces that tag with text and the current server time. That test plugin replaces mfunc code that would look like this, excluding the necessary code to hook on to wp_footer and print it.

<!--mfunc echo "<!-- Hello world at " . date( 'H:i:s' ) . " -->"; -->
<?php echo "<!-- Hello world at " . date( 'H:i:s' ) . " -->" ?>

WP Super Cache has it’s own action hooks using add_cacheaction() and do_cacheaction(), and work like WordPress actions or filters. The reason the plugin needs those is because they are available before WordPress is loaded. They allow developers to hook into the plugin from the very start of the PHP process and modify how it works using plugins. Those plugins are usually copied into wp-super-cache/plugins/ but I encourage you to move that directory elsewhere because when WordPress updates the plugin it will delete any custom changes you make. The next time a new version of WP Super Cache comes out WordPress will delete the wp-super-cache folder, replacing it with the new update. In your wp-config.php set $wp_cache_plugins_dir to the location of the new plugins directory.

If you use this filter system in your own plugin for distribution do not ever define the template tag for the user. Let the user decide what it is or generate a random tag and save it somewhere. It’s important to keep the tag secret so visitors cannot trigger your function maliciously. It is however better than the remote user running any code they like as was the case with mfunc!

I hope to release a new version with this code late next week. If your plugin or site uses mfunc please download the development version on a test server and start the process of updating your code.
On the other hand, if you don’t want to update your mfunc tags you could try W3 Total Cache instead. It uses the mfunc tag with a secret code.