Last Updated: December 12, 2018
·
3.809K
· paptamas

How I used file versioning and CloudFront on my latest side project: reme.io

CloudFront is a great service. I love it. A lot of websites and applications use it to serve static content.

The only one challenge I met while using CloudFront was to make it always serve the latest version of my files.

There are several solutions to keep CloudFront up-to-date with the changes you make on your files.

I. Create an invalidation request

Basically you tell CloudFront that some of your files are outdated, and should be updated. This method is working fine, but when you have to do this frequently it becomes a pain in the ass. It’s definitely not the way to go while your project is under heavy development and you push to production new versions of your assets daily.

II. Use versioned object names

This means that you include a version identifier in your object names.
For example:

It’s simple. But you definitely want to automate this process to make your life easier.

Here is how I solved this problem for our latest side project: reme.io.

On front-end I use Grunt to concat+minify my javascript, compile+clean my LESS, and optimize my images.

All the output files are going to the public_html/dist/#some_version_hash folder.

In my Gruntfile.js I defined a distTarget option based on distVersion:

module.exports = function(grunt) {  
  grunt.option("distVersion", "1.0.0");
  grunt.option("distVersionHash", require("crypto").createHash("md5").update(grunt.option("distVersion")).digest("hex"));
 grunt.option("distTarget", "dist/" +    grunt.option("distVersionHash"));
 ...

And I use it like this:

...
concat: {  
  dist: {
    src: [
      // List of JS files to concat
    ],
    dest: "<%= grunt.option('distTarget') + '/js/app.js' %>"
  }
},
uglify: { 
  dist: {
    files: {
     "<%= grunt.option('distTarget') + '/js/app.min.js' %>”: ['<%= concat.dist.dest %>']
    }
  }
}
...

After I run grunt build, I will have my minified javascript file under:
public_html/dist/47cd76e43f74bbc2e1baaf194d07e1fa/js/app.min.js.

On the back-end I do something very similar. In my main view:

<?php
  $distVersion = '1.0.0';
  $distTarget = 'dist/' . hash("md5", $distVersion);
  $cloudFrontURL = 'http://d1mnizi6d3fc4n.cloudfront.net/';

  $cssURL = App::environment('production')
    ? $cloudFrontURL . $distTarget . '/css/app.min.css'
    : URL::asset('$distTarget . '/css/app.css');

  $jsURL = App::environment('production')
    ? $cloudFrontURL . $distTarget . '/js/app.min.js'
    : URL::asset('$distTarget . '/js/app.js');
?>
<!doctype html>
<html ng-app="app">
  <head>
    <meta charset="utf-8" /> 
    <link rel="stylesheet" href="{{$cssURL}}"/>
    <script src="{{$jsURL}}"></script>
    ...
  </head>
  <body>
  ...
  </body>
</html>

That’s it. Now before I push a new version of my static files to production I simply increment the distVersion in my Gruntfile.js and in my main PHP view, and I’m good to go.

If you have any questions or ideas to improve this workflow just let me know ☺