Setting up a CDN for your application assets is easy nowadays. This guide will walk you through using Amazon CloudFront as an asset CDN for your Rails application.
There are other guides out there today; Heroku’s CloudFront guide is pretty good, but I think misses a few key points about CORS and denying requests outside /assets
. This guide should fill those in.
Set up CloudFront
Sign up in aws.amazon.com and create a new CloudFront distribution. This will get you a subdomain (like d1h2t3n5.cloudfront.net
) that will act as a caching proxy to your actual site. You may opt to use your own domain names if you like.
Custom configuration
Make sure that OPTIONS
is also being passed through. This will allow CORS requests through (see next section). Also, enable “compress automatically” to let CloudFront handle gzip compression for you.
- Origin Settings
- Origin domain name:
www.yoursite.com
- Origin domain name:
- Default Cache Behavior Settings
- Allowed HTTP Methods:
GET, HEAD, OPTIONS
- Cached HTTP Methods: Turn on
OPTIONS
- Compress Objects Automatically:
on
- Allowed HTTP Methods:
Set up asset host
This will make image_tag
, asset_url
and other asset-related helpers point your assets to your CloudFront distribution. Do this only for production.rb
.
# config/environments/production.rb
config.action_controller.asset_host = '<YOUR DISTRIBUTION SUBDOMAIN>.cloudfront.net'
Serve static assets
Enable the serving of static assets. You will want to do this if you’re using Heroku or any 12-factor-style deployment. If you use a reverse proxy like Nginx or Haproxy, skip this section and configure your reverse proxy to handle CORS instead.
# config/environments/production.rb
config.serve_static_assets = true
config.static_cache_control = 'public, max-age=31536000'
Enable CORS in assets
If you use serve_static_assets
, you will need to enable cross-origin requests for assets. This will prevent issues like Firefox not loading custom icons and fonts.
Install the rack-cors
gem
Use the rack-cors gem to enable cross-origin requests. At time of writing, it is at version 0.4.0.
# Gemfile
gem 'rack-cors'
Configure rack-cors
This will make assets accessible from any website. You want to enable this because you’d want yoursite.com
to be able to load assets out of <id>.cloudfront.net
.
# config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, 'Rack::Cors' do
allow do
origins '*'
resource '/assets/*',
headers: :any,
methods: [:get]
end
end
Deny everything but /assets
Set up your app to disallow Cloudfront from fetching anything but /assets
. This uses User Agent detection; see CloudFront’s docs on User-Agent headers for information.
If you miss this step, you’ll be able to access the rest of your site in your CloudFront URL. While those aren’t public, you’d best have them secured as it can open up security flaws and possibly lead to SEO penalties.
# config/routes.rb
Rails.application.routes.draw do
match '*path', via: :all, to: 'errors#not_found',
constraints: CloudfrontConstraint.new
...
# app/services/cloudfront_constraint.rb
class CloudfrontConstraint
def matches?(request)
request.env['HTTP_USER_AGENT'] == 'Amazon CloudFront'
end
end
# app/controllers/errors_controller.rb
class ErrorsController
def not_found
raise ActiveRecord::RecordNotFound
end
end