How to speed up your Rails app with Cloudfront and the asset pipeline
A common trick I use with client work to speed up asset delivery is use cloudfront as an asset host. I'm surprised this technique isn't in more widespread use as it reliably makes for snappier user experiences, especially users with a fresh cache.
This guide assumes you're using the Rails asset pipeline to package and serve your assets. If you're not using the asset pipeline, whatever solution you use to achieve the same result will need to fingerprint asset filenames with some sort of unique identifier for it to work smoothly with Cloudfront or a CDN like it.
Don't use S3 to serve assets!
A common mistake I see in websites is serving static assets from S3.
S3 is a place for storing files, and I've only experienced bad download times from it. Directly serving web traffic is not the use-case that S3 is designed for so you shouldn't be surprised if you get less than optimal performance from it.
How Amazon Cloudfront works
These are servers optimized for serving static files and are dotted around the globe. Using DNS, any requests for a particular asset are routed to the edge cache location closest to the user.
To work with Cloudfront you first need to set up a distribution. Each distribution is assigned a domain that you can use to serve your static assets.
Typically, the domain name you get for a new Cloudfront distribution looks like the following:
Setting up a CNAME from
trivial, so you don't need to use the nasty domain that Cloudfront gives you for
Each distribution has to be configured with an origin. The origin is where your distribution will forward requests to if it doesn't have a cached copy of the file that was requested. After the first request for an asset, it's served out of the edge cache.
It used to be that Cloudfront could only be configured to use S3 buckets as origin servers but they've supported custom origins for a while. So if you configure the origin server of your distribution to be:
Then when you make the following request to a CF distribution:
The first time it receives that request, the distribution will forward that request to:
And cache whatever it finds there for future requests.
Setting up a new distribution
Setting up a new distribution for your Rails app is as easy as logging into the AWS management interface, clicking 'Cloudfront' and then 'Create distribution'. You should be presented with a dialog that looks something like this:
The default settings should be fine for your application. If you want to set up
a custom domain for using the distribution (e.g.
can enter it in the field labeled 'Alternate Domain Names' but you can always
add this later if you prefer.
Once you've clicked 'Create Distribution', the setup process will begin. This takes in the region of five to ten minutes to complete. The AWS UI will give you an indication of the distributions current status and tell you the domain name assigned to the distribution.
Before moving on to the next stage, I'd recommend testing manually to see that the distribution is working correctly. To do this, take the URL of one of your assets and attempt to retrieve it using the distribution domain.
For example, if your Rails application is serving a css file at the following URL:
And you've configured the distribution to have the custom origin of
my-rails-app.com, then you should be able to get that file at the following
If this doesn't work then either the distribution setup isn't quite finished or you've fat-fingered the configuration. Make sure you can get access to your assets before you setup your Rails application to work with Cloudfront.
Making Cloudfront work with Rails
The Rails asset pipeline has the handy feature of compiling your assets and then appending a hash of the contents to the filename. This means that if the contents of the file change, then the filename changes too. This is perfect for working with an edge cache like Cloudfront, as it never needs to expire objects.
You can make Rails serve static assets from your newly minted cloudfront
distribution by changing one line in
production.rb. Specifically, set your
asset_host to the domain name of your new Cloudfront distribution:
# config/environments/production.rb config.action_controller.asset_host = 'your-dist-url.cloudfront.net'
This has the effect of making all the links you send use the configured asset host instead of that of your own website. For example, where your main application stylesheet may have been served like this in your HTML head:
<link href="/assets/application-99b3.css" rel="stylesheet">
The href will now have the configured asset host prepended:
<link href="http://your-dist-url.cloudfront.net/assets/application-99b3.css" rel="stylesheet">
As mentioned before, you can setup a CNAME record using DNS provider of choice
to use a nicer looking URL for your asset host. This blog for example uses
cdn.happybearsoftware.com which is just a CNAME for the Cloudfront
No syncing to S3 required
Since your Cloudfront distribution will only ask your application for a given asset once, it's not worth syncing them to S3.
No cache invalidations required either!
Remember that the asset pipeline fingerprints each asset with a hash of the contents, so you don't need to worry about serving stale assets to your users. When a new version of a file is referenced in your HTML, it will have a unique hash of its contents as a part of the filename. Since it has a different filename, Cloudfront recognizes your new version as a distinct resource.