Cache busting with Rails and Cloudfront

My colleague and I ran into a problem with deploying a Rails app that utilized a custom origin on Amazon Cloudfront.

The default Rails cache-busting technique appends a timestamp query string to the end of an asset’s source ( ex. all.css?1302137895 ), which works great in most cases but will not trigger an origin-fetch from Cloudfront, which can cause deprecated files to be served out for up to 24 hours.

There is a good work around that’s outlined in this article: CloudFront custom origin server with Rails, Jammit, and Apache, which dynamically rewrites the asset_path using the REVISION file that Capistrano creates for you when deploying.

While this technique works well, it’s not compatible with the built-in Rails action_controller caching ( combining javascript and stylesheet files into one to reduce HTTP requests )

Building off the opengovernment article, I modified the asset_path to only version the path for images:

environments/production.rb:

  config.action_controller.perform_caching = true

  REVISION = File.exists?('REVISION') ? %x{cat REVISION | cut -c -7}.rstrip : false

  config.action_controller.asset_path = proc do |asset_path|
   if asset_path =~ /^\/images\//
    "/release-#{REVISION}#{asset_path}"
   else
      asset_path
   end
  end

For the asset helper methods ( stylesheet/javascript_link_tag ) I created an application helper to include the revision number in the filename that is passed to the :cache parameter:

def cache_name(name = 'all')
  return false unless File.exists?('REVISION')
  name + '-r' + %x{cat REVISION | cut -c -7}.rstrip
end

In the application layout:

<%= stylesheet_link_tag :all, :cache => cache_name %>
<%= javascript_include_tag :all, :cache => cache_name %>

This way, all the image paths will be versioned and trigger an origin-fetch from Cloudfront immediately upon deployment.

The javascript and css files are combined into a single, versioned filename which will also trigger an origin-fetch.

I also threw in the smurf gem to automatically minify the unified css and javascript files for extra goodness.

Now, the application servers only spit out 5-8k chunks of HTML and Cloudfront handles the rest of the assets.