Introducing Cache Buddy: a companion for your WordPress page caching solution

WordPress is, by default, completely dynamic. On every page load, a bunch of “work” happens. Cookies are read. A database is queried. Content is transformed. All of this makes WordPress very powerful and flexible. But for sites that get a lot of traffic and mostly just need to crank out the same pages for everyone, this dynamic nature can become a challenge.

The common solution to this is to layer a page cache on top of WordPress. Batcache, W3 Total Cache, and WP Super Cache are examples of page caches built as WordPress plugins. Varnish, Nginx fastcgi caching, and CDNs like Akamai or Cloudflare are examples of page caching that happens outside of the WordPress layer. They store the HTML that WordPress generates for a given URL and then store it for later, so that when people request that URL in the future, they can just get the cached version, for little or no work on WordPress’ part.

But these page caching solutions all share the same downside: they can’t cache pages for logged-in WordPress users or users with WordPress comment cookies. Why not? Well, because WordPress looks at these cookies and alters the page based on them. A logged in user will see the WordPress toolbar at the top, which is customized to them. Users with more privileges might see “edit” links next to content that they can edit. And returning commenters will see their name, e-mail, and URL helpfully filled in to comment forms. All these things change the output of the page, such that it wouldn’t be worth it for a page cache to hold on to that page — it would only be of use to the individual visitor who triggered it. So all of these page caching solutions have rules that make them “skip” the page cache if a user has a WordPress comment cookie, or a WordPress user account cookie (and also a post password cookie, though this is an infrequently used feature). If a site has an active commenting community or has open registration (or required registration), this means that a much smaller percentage of page views can be cache hits. Instead, they are the dreaded cache miss, and they fall back to having WordPress generate a dynamic page.

The difference between a cache miss and a cache hit is not small. A cache hit takes minimal effort for the server, and can be delivered to the user much faster. It can be the difference between 1 second and 0.002 seconds. Five hundred times slower. Dynamic views keep the server connection open for longer, and take up CPU cycles. This can snowball under heavy load. Pages start taking longer, and because they start taking longer, less CPU is available. Eventually they can time out, or the server can run out of connections. Not good. You want cache hits, during a situation like this, but if the traffic isn’t anonymous (non-comment-cookie, non-logged-in-cookie), the available caching solutions just give up.

I’ve been solving this issue for years with custom caching solutions that strip the customizations from the page, so that the cache can be configured to serve one static page to everyone. Now, I’ve moved these techniques into a plugin, and I’m calling it Cache Buddy.

Cache Buddy works by doing the following:

  1. Changes what paths logged-in cookies are set for (so they work in the WordPress backend, but don’t exist on the front of the site).
  2. Sets custom cookies with relevant information about the logged-in user, on the front of the site, making these cookies JavaScript-readable.
  3. Sets custom cookies for commenters (again, JavaScript-readable), and doesn’t set the normal WordPress comment cookies.
  4. Uses the information from these JavaScript cookies, plus some comment form magic, to recreate the comment form experience users would get from a dynamic page.

This means that you can log in to WordPress, and then go a view a post’s comment form, and see “You are logged in as Mark. Log out?”. Or you can be a non-account-having commenter who has commented, and your information will be filled in. Or maybe the site requires registration, and you’re not signed in. You’ll see the normal prompt to sign in. But here’s the kicker: all of these pages are the same page, and will be cached by page caching solutions. The customizations are all done in JavaScript, using the custom (and unknown to WordPress-optimized page caches) cookies that Cache Buddy sets.

What about the toolbar?

Well, by default, Subscriber and Contributor users won’t see it. But it honestly isn’t very useful to them anyway. But Authors, Editors and Administrators (who should be a very small percentage of viewers) will still get dynamic page views like they do now, and they’ll see the toolbar.

What about BuddyPress?

Good luck. Some plugins customize the page so much that all views really do need to be dynamic. Object Caching is your friend, for these cases.

Is this for every site?

No. If you have a BuddyPress site or an e-commerce site, you may honestly need WordPress logged-in cookies available on the front of your site. But if you’re just running a blog/CMS site with a significant number of commenters and logged-in Subscribers, this plugin could massively speed up your site, because requests that had to always be dynamic before, can now be served from a page cache.

What about the “Meta” widget?

Not currently supported, but I’m hoping to add support for it.

What about other logged-in site customizations?

The user will appear to by an anonymous visitor. But you could recreate them in JS by reading the cookies that Cache Buddy sets.

Ask Mark Anything

People ask me a lot of questions. About WordPress and web development for sure, but also about other topics. I’ve decided to try a little experiment: a public way to ask me questions. Zach Holman from GitHub had the idea to use a GitHub issue tracker for this very purpose, and I think it looks like a splendid idea.

Benefits:

  • Allows for more in-depth discussions than Twitter (but you can still talk to me on Twitter for quick questions).
  • Is public (as opposed to e-mail).
  • Forces me to deal with questions.

Now, note that this doesn’t mean I want you to treat me like your personal Google-searcher or WordPress code grepper! But if you think there is a WordPress (or other) topic that I am uniquely qualified to address, just ask.

Don’t use template_redirect to load an alternative template file

template_redirect is a popular WordPress hook, for good reason. When it runs, WordPress has made its main query. All objects have been instantiated, but no output has been sent to the browser. It is your last stop to hook in and redirect the user somewhere else, and the best place to do so if you need full knowledge of the queried objects. But what it is not good for is loading an alternative template.

I see code like this a lot:

add_action( 'template_redirect', 'my_callback' );

function my_callback() {
  if ( some_condition() ) {
    include( SOME_PATH . '/some-custom-file.php' );
    exit();
  }
}

The problem with this code is that anything hooked in to template_redirect after this code isn’t going to run! This can break sites and lead to very odd bugs. If you want to load an alternative template, there’s a filter hook for that: template_include.

add_filter( 'template_include', 'my_callback' );

function my_callback( $original_template ) {
  if ( some_condition() ) {
    return SOME_PATH . '/some-custom-file.php';
  } else {
    return $original_template;
  }
}

Same effect, but doesn’t interfere with other plugin or theme code! This distinction should be easy to remember:

  • template_redirect is for redirects.
  • template_include is for includes.

Six Apart Suspends Movable Type Open Source Project

Six Apart announced that they are suspending the free and open source version of Movable Type. Here’s what I had to say about them revealing the free and open source version of Movable Type, back in 2007.

Note that this also allows Six Apart at any time in the future to say “As of today, we are no longer releasing a GPL version of Movable Type.” And that would require that someone fork the code in order to proceed with development. WordPress can’t easily do that, as it is not owned by a single legal entity.

What a GPL’d Movable Type means for WordPress

When I wrote that, I honestly didn’t think it was going to happen. I was just saying that it was an option that was open to them. But here they’ve gone and done it. What a bizarre saga this has turned out to be. “Life is funny”, remarks Anil Dash, former Six Apart Chief Evangelist.

Will this start a conversation about copyright assignment on open source projects, or is this a non-event? If a project has a strong open source development community, an attempt to close it down should result in a fork of the project. As it happens, Movable Type Open Source was already forked. Several prominent Movable Type people created Open Melody back in 2009. Don’t bother clicking that link. As of this writing the site is non-operational. The Open Melody Twitter account just recorded its first activity in over two years. A retweet of this:

I guess we’ll find out!

Fragment Caching in WordPress

Fragment caching is useful for caching HTML snippets that are expensive to generate and exist in multiple places on your site. It’s like full page HTML caching, but more granular, and it speeds up dynamic views.

I’ve been using this fragment caching class for a few years now. I optimized it around ease of implementation. I wanted, as much as possible, to be able to identify a slow HTML-outputting block of code, and just wrap this code around it without having to refactor anything about the code inside.

Implementation is pretty easy, and you can reference the comment at the start of the code for that. The only thing to consider is that any variables that alter the output need to be build into the key. It should also be noted that this code assumes you have a persistent object cache backend.

WordPress 3.6: shortcode_atts_{$shortcode} filter

Since WordPress 3.6 is in beta, I thought I’d use this nearly-abandoned blog (hey, been busy working on WordPress!) to talk about some of the cool stuff for developers. For the first installment, check out the new shortcode_atts_{$shortcode} filter. The shortcode_atts() function now accepts a third parameter — the name of the shortcode — which enables the running of this filter. All of the core shortcodes now pass this parameter.

This filter passes three things:

  1. $out — the output array of shortcode attributes
  2. $pairs — the array of accepted parameters and their defaults.
  3. $atts — the input array of shortcode attributes

Let’s look at what we can do with this. One thing is that you can dynamically set or override shortcode values. You an also define new ones, and transpose them into accepted ones. Let’s look at the “gallery” shortcode for example. What if there was a gallery of images that you would reuse. Instead of picking the images each time, you could have a plugin that gives that set of attachment IDs a shortcut name. Then you could do [gallery foo=my-gallery-name], which the plugin would convert to a list of IDs. Or, you could enforce a certain number of gallery columns on the fly. Let someone set it theme-wide, without having them go back and change their shortcodes.

What other uses can you think of?

Now, if you’re a plugin or theme author who provides their own shortcodes, you should immediately start providing this third parameter to your shortcode_atts() calls (since it is an extra parameter, you can do this without a WordPress version check). Maybe it’ll reduce the number of times people need to fork your code just to add an option to your shortcode!

WP Help 1.0

My WP Help plugin just got a huge update. Version 1.0 is really worth checking out. I’m quite proud of it.

WP Help

WP Help is a plugin for creating documentation for display within the WordPress admin. Many WordPress installs are customized, and it’s really helpful to have a centralized resource for documenting those features. You can create documents about creating content, editing content, moderating comments, or whatever you want! If you have clients who can’t seem to remember how to do X, you should install WP Help and document it for them. WP Help is powered by WordPress Custom Post Types, so you create content using the full WordPress editor.

Document Syncing

Oh yeah. This is the feature you’ve all been waiting for. If you have a standard set of help documents you want to use on multiple sites, this lets you do that. Create the documents, grab the (secret) sync URL for that site, and then plug that URL in to other sites. Those other sites will automatically pull down those documents, and keep them up-to-date (even handling new documents, deleted documents, renamed documents, and re-parented documents). Any internal links in the original document will be rewritten to be local to the destination WP Help install. So go ahead and use the WP internal linking functionality on your source site and know that those links will work on all the destination sites!

Menu Placement

The menu item for the help documents can now be placed in one of four locations:

  1. As a Dashboard submenu
  2. Top level, above the Dashboard
  3. Top level, below the Dashboard
  4. Top level, at the bottom

You get a live preview of this (yeah, I know, super fancy).

Menu Name

The menu name (and page title) can be changed. Just doubleclick the page title, edit it, and hit return. Boom. Again, this has a live preview.

Topics List Name

Likewise the topics list header can be renamed. Doubleclick, edit, return. Live preview.

Edit Links & Management Links

If you can edit help documents, you’ll get an edit link. When editing, there is a handy “Manage” link to jump you back to the documents management interface. Navigating in general has been improved quite a bit.

Dashboard Widget

There is now a simple dashboard widget, listing all of your help documents.

Better Default Access

A lot of you said that you have Contributor-level users who need documentation just like authors do. So now they can view documentation by default (there’s a hook if you want to change the capability required to view help documents).

Lots of Little Tweaks

There are numerous little tweaks to improve your experience. Check out out!

Roadmap

Things I’m considering:

  • Restricting individual documents to users with a certain level of access
  • Multiple sync sources

Any other ideas?