Skip to content

Instantly share code, notes, and snippets.

@bensheldon
Last active March 2, 2020 20:20
Show Gist options
  • Save bensheldon/5031827 to your computer and use it in GitHub Desktop.
Save bensheldon/5031827 to your computer and use it in GitHub Desktop.
Serving Rails Static Assets on Heroku using a CDN with gzip and cache control

Optimizing Rails Static Assets on Heroku Cedar

Serving Rails static assets is hard, especially on Heroku Cedar where Heroku forces Rails to serve static assets itself (which isn't particularly performant nor worth your dyno-dollar)---this is different than Heroku Bamboo which used Varnish and is no more. To make the best of the situation, I highly recomend:

  1. Using the Heroku-Deflater gem which will selectively gzips assets (it gzips text-based files; and excludes images or binary files, which can actually be bigger when gzipped)

  2. Configure your production environment to set cache-control headers and get out of denial about how static assets are being served on Heroku Cedar

  3. Use AWS Cloudfront (or a CDN of your choosing) to serve the assets. Cloudfront is great because you can use the same Distribution to serve multiple origins (e.g. it can pull both from your S3 bucket where you store uploaded CarrierWave/Paperclip assets AND the website's precompiled assets). Also, Cloudfront is no more expensive really than S3 (300GB/month transfer on S3: $38; Cloudfront: $42) and much much faster.

This gist recommends using your website as the CDN origin, rather than precompiling your assets and uploading them to a S3 bucket. Using your website as the origin is simpler and avoids any situations where your assets are out of sync with your website.

Cloudfront Configuration

  1. Create a new distribution of type "Download"
  2. Set the origin to be either (don't worry, if you need both we can add it after the initial distribution creation:
    • your application's site (e.g. yourapp.herokuapp.com or your custom domain)
    • your S3 bucket (it should be in the suggestion dropdown) for carrierwave/paperclip
  3. Accept all the other default stuff and click "create distribution"
  4. (if you're using 2 origins, add your other origin under the Origin's tab)
  5. Setup the "Behaviour" so that we're only distributing the assets directory through the CDN. Do this by adding /assets/* to the Path Pattern for the origin that corresponds to your website
gem 'heroku-deflater', :group => :production
# Heroku Cedar automatically injects this into your configuration so you might
# as well be honest about it
config.serve_static_assets = true
# You definitely want to set cache-control headers; you can override them with
# Cloudfront, but best to do it right at the origin (and other CDNs might not
# be so helpful
config.static_cache_control = "public, max-age=31536000"
# Add the cloudfront hostname (including the `http(s)://` that you have
# configured to serve these assets
config.action_controller.asset_host = ENV['CDN_HOSTNAME'] # e.g. `http://gobbledy.cloudfront.net`
# ... the following is are defaults but probably good to double-check...
# Compress JavaScripts and CSS
config.assets.compress = true
# Don't fallback to assets pipeline if a precompiled asset is missed
# (remember, your server will throw an exception if an expected
# asset isn't precompiled, so test your assets in staging)
config.assets.compile = false
# Generate digests for assets URLs
config.assets.digest = true
@wrburgess
Copy link

Note: If you utilize Heroku Review Apps, the temporary nature of the apps may not work with these settings:

This instruction may help:

# config/environments/staging.env AND/OR config/environments/production.env

unless ENV["REVIEW_APPLICATION"] == "true"
  config.action_controller.asset_host = ENV["CDN_HOSTNAME"]
end

See: http://secondforge.com/blog/2015/09/22/using-cloudfront-with-heroku-pipeline-review-apps-and-rails/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment