Filename cache busting for WordPress styles and scripts

To embed custom CSS styles and scripts in WordPress you should use the wp_enqueue_script(), wp_enqueue_style(), wp_register_script() and/or wp_register_style() functions. Each of these functions allows you to define a version. By default it's the version of WordPress. The version identifier will be in the URL to the script as a query string.

The version identifier is used to expire the URL. Since the browser detects the new URL as a new resource, it will use the new instead of the cached resource.

Sadly not all endpoints respect the query string. From Google Developers:

Most proxies, most notably Squid up through version 3.0, do not cache resources with a "?" in their URL even if a Cache-control: public header is present in the response. To enable proxy caching for these resources, remove query strings from references to static resources, and instead encode the parameters into the file names themselves.

So the goal is to encode the version identifier into the filename without renaming the resource on the filesystem. This is where the following plugin comes in.

<?php
/**
 * Plugin Name: Filename-based cache busting 
 * Version: 0.2
 * Description: Filename-based cache busting for WordPress scripts/styles.
 * Author: Dominik Schilling
 * Author URI: http://wphelper.de/
 * Plugin URI: http://wpgrafie.de/880/
 *
 * License: GPLv2 or later
 * License URI: http://www.gnu.org/licenses/gpl-2.0.html
 *
 * 
 * Extend your .htaccess file with these lines:
 *
 *   <IfModule mod_rewrite.c>
 *     RewriteEngine On
 *     RewriteBase /
 *
 *     RewriteCond %{REQUEST_FILENAME} !-f
 *     RewriteCond %{REQUEST_FILENAME} !-d
 *     RewriteRule ^(.+)\.(.+)\.(js|css)$ $1.$3 [L]
 *   </IfModule>
 */


/**
 * Removes the `ver` query string of the source and places it into
 * the filename. Doesn't change admin scripts/styles and sources
 * with more than the `ver` arg.
 *
 * @param  string $src The original source 
 * @return string
 */
function ds_filename_based_cache_busting( $src ) {
	// Don't touch admin scripts
	if ( is_admin() )
		return $src;
	
	return preg_replace(
		'/\.(js|css)\?ver=(.+)$/',
		'.$2.$1',
		$src
	);
}
add_filter( 'script_loader_src', 'ds_filename_based_cache_busting' );
add_filter( 'style_loader_src', 'ds_filename_based_cache_busting' );

You can grab the plugin from GitHub. After that you have to extend your .htaccess file with the lines from the docblock.

If you are using nginx you can use these lines:

location ~ ^(.+)\.(.+)\.(js|css)$ {
    alias $1.$3;
}

Comments are closed.

5 comments

  1. kaidez

    Great article...thanks for sharing.

    Quick question/confirmation: will this also encode comment-reply.js, which is cache-busted?

  2. Dominik

    @kaidez:

    Yes, it will be changed too. For example: /wp-includes/js/comment-reply.min.3.5.2-alpha.js

  3. stef

    Does the function also workwithout adding the last code to .htaccess? I think it is because it's working!

  4. Angel Luv

    This is a pleasant experience reading post on your informative blog. I'm getting educated more and more about blogging here.

  5. Leena Dasot

    wpenggineer.com, great team, thanks for taking such good care of this website/blog! Your all information are really satisfying. It so hard to find good articles on best deals. Thanks