Recently, I had a strange issue where some pages on my multisite were being cached, and others were not. There seemed to be no clear pattern as to what got cached and what failed the cache test.
At first, it seemed a bit daunting to jump into the w3tc page caching code, but it actually turned out to be a somewhat fun and surprisingly painless process. I talk about my debugging efforts here, as well as how the w3tc page caching process works under the ‘Page Enhanced’ cache setting.
Note – This is an advanced tutorial where I make changes to core plugin files. Do not attempt any of this unless you have ftp access to your server, and are very comfortable with restoring plugin files back to their original state.
w3tc Page Cache – How It Works
The first line of redirection is through the server .htaccess file. In particular, if a cached file already exists, rewrite rules in our domain root directory will point a page request directly to the appropriate file.
In version 0.9.2.8, cached files are kept in
wp-content/cache/page-enhanced/mysite.com/
The rewrite rule looks something like this –
RewriteRule .* "/wp-content/cache/page_enhanced/%{HTTP_HOST}%{REQUEST_URI}/_index%{ENV:W3TC_UA}%{ENV:W3TC_REF}.html%{ENV:W3TC_ENC}" [L]
advanced-cache.php
If the cache file does not exist, then WordPress starts up and the page caching process starts with the wp-settings.php file. In particular, the file should contain the following lines of code-
// For an advanced caching plugin to use. Uses a static drop-in because you would only want one. if ( WP_CACHE ) WP_DEBUG ? include( WP_CONTENT_DIR . '/advanced-cache.php' ) : @include( WP_CONTENT_DIR . '/advanced-cache.php' );
If our site is not caching any pages at all, then we may want to check the wp-settings.php file to see if the cache code above is present. This code is necessary for page caching to work in w3tc.
Sometimes, the advanced-cache.php file may be missing from our wp-content directory. When this happens though, there is usually a notice in the w3tc ‘Performance’ menus.
At the bottom of the advanced-cache.php file, an instance of the W3_PgCache object is created, and the process function is run to handle caching for a page request.
W3_PgCache Object
The W3_PgCache object is stored in the w3-total-cache/lib/W3/PgCache.php file. It contains the guts of the page caching process, so this is where we will be concentrating most of our effort.
I start my debugging process by opening the PgCache.php file, and then putting in a debug echo statement at the top of the process function. I encapsulate my echo messages within HTML comment markers so that it does not disrupt layout and content of my site pages.
echo "<!-- Start of process function -->";
If I am running on a multisite configuration, then I use blog_id to limit debugging to only a test sub-site.
if (w3_get_blog_id() == 2) { echo "<!-- Start of process function -->"; }
In general, it is better to do our testing and debugging on a test site, but since I could not replicate the problem I was having on one of my live sites, I had no choice.
Next, I continue to move down my debug test statement until I reach a section where it fails.
Note – If we are in the ob_callback function, then we cannot use echo, and need to append our message to the current buffer instead.
if (w3_get_blog_id() == 2) { $buffer .= "<!-- Ob callback -->"; }
My page source looks something like this –
<!-- Start of process function --><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US"> ... <!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/ ... Served from: mysite.com @ 2013-03-28 06:06:34 by W3 Total Cache --> <!-- Ob callback --> <!-- W3 Total Cache: Page cache debug info: Engine: disk: enhanced ... Content-type: text/html -->
Depending on the location of my debug statements, the messages could appear at the top or bottom of my html source file.
Why Some Files Were Cached but Not Others
Finally, I tracked the problem down to the _is_cacheable_content_type function. It was failing because there is a case-sensitive check for the ‘Content-Type’ string in the headers_list. The problem is that sometimes, the headers list contains ‘Content-type’, with a lowercase t.
Once I located the issue, the fix was really simple – I changed strpos in the _is_cacheable_content_type function to stripos.
if (strpos($header, 'Content-Type') !==false) {
now becomes
if (stripos($header, 'Content-Type') !==false) {
Now, all the files are properly cached.
Other Useful Debugging Tools
When debugging on a multisite setup, it may also be helpful to turn on debugging information for just a single test blog. I did this by adding the code below to my theme init action hook.
// Add to theme init action hook if (w3_get_blog_id() == 2) { $w3_pgcache = w3_instance('W3_PgCache'); $w3_pgcache->_debug = TRUE; }
Happy Debugging!