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:
-
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)
-
Configure your production environment to set cache-control headers and get out of denial about how static assets are being served on Heroku Cedar
-
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.
- Create a new distribution of type "Download"
- 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
- your application's site (e.g.
- Accept all the other default stuff and click "create distribution"
- (if you're using 2 origins, add your other origin under the Origin's tab)
- 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
(fair warning, this is a biased comment)
You could, just as easily, use an App server that serves static files, such as
iodine
.The iodine server is written in C and the static file service never enters the Ruby realm, making it very performant.
Iodine also recognizes pre-compressed assets, preferring
.gz
files when available and supported by the client (i.e.index.html.gz
).Simply add
iodine
to the Gemfile:Edit the procfile to run:
(note the
-w
allows for multi-process concurrency and the-t
allows for multi-threaded concurrency, the negative value-1
is equal to the number of available CPU cores).