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:

  1. Make sure to specify your normal build task (default in my case) as a dependency in gulp.task, otherwise default and cachebust tasks will run in parallel and MD5 hash may be calculated before new assets are built.
  2. 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:

Running gulp production in 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!