Adding fingerprints to assets with Gulp and DeployBot
While working on a new Wildbit website last week I spent some time tweaking its performance. Caching CSS, JS, and images for a long period of time is one of the most important things you can do to create a fast loading website. It’s relatively easy to configure on most web servers, but comes with a downside — you’ll need to invalidate cached files whenever they change.
The most common way to do this is by adding a unique fingerprint of file content to the name, either by renaming the file (main_9848a911e2.css
) or using a query parameter (main.css?9848a911e2
). The file’s fingerprint will change when you’ve changed the contents of a file. MD5 is the most common implementation of this idea, so it was my natural first choice. This method is a perfect fit for us, as we want to invalidate only those files we changed and keep unchanged files in cache as long as possible.
At Wildbit, we run our landing sites on Craft CMS and build assets with either Gulp or Grunt. There is a very handy Gulp plugin gulp-md5-assets that I used for fingerprinting assets. It goes through every CSS or JS file, calculates its MD5 hash, then searches for references to this file in templates and appends the hash as a query parameter to their names.
Adding it is pretty simple. First, add it as a development dependency:
npm install --save-dev gulp-md5-assets
Then update your gulpfile.js
file with paths to assets and templates. Mine are standard for Craft, but you’ll need to change paths to templates and assets for WordPress or any other CMS:
var md5 = require("gulp-md5-assets");
gulp.task('cachebust:css', ['default'], function () {
return gulp.src('./html/stylesheets/**/*.css', {base: './html/'})
.pipe(md5(10, './craft/templates/**/*.twig'));
});
gulp.task('cachebust:js', ['default'], function () {
return gulp.src('./html/javascripts/**/*.js', {base: './html/'})
.pipe(md5(10, './craft/templates/**/*.twig'));
});
gulp.task('production', ['default', 'cachebust:css', 'cachebust:js']);
I added a new task production
that updates templates after running all standard build tasks. A couple of things that I learned along the way:
- Make sure to specify your normal build task (
default
in my case) as a dependency in gulp.task, otherwisedefault
andcachebust
tasks will run in parallel and MD5 hash may be calculated before new assets are built. - Return a promise or event stream from tasks that are a part of your default task. Without those async run hints, Gulp may not know when
default
task is completed.
Never run it locally as a part of your standard build — nobody wants to clutter Git history with fingerprints updated after every code change. That’s the beauty of DeployBot — it pulls “clean” templates from the repository, updates them inside a build container, and then uploads to your server. I run production
task during deployment in DeployBot’s build container:
That’s it — all references to your CSS and JS files on live website should be appended with a fingerprint as query parameter. Hopefully, that will help make your website a little bit faster and avoid annoying cache issues!