Untangling the Rails Asset Pipeline, Part 2
Making it work in production.
In the first article of this series I reviewed some issues you may run across while developing a Rails application using the asset pipeline. Today let’s look at what happens in production.
This article is the second of a four-part series on the Rails asset pipeline. Read the previous installment: Part 1: Caches and Compass.
Understanding why assets:precompile exists and how assets are served in production
An out-of-the-box Rails app will not work in production unless you run bundle exec rake assets:precompile
first. You probably knew that already. But what does this rake task actually do, and why is it needed? The asset pipeline guide has a good explanation. Even if you’ve read the guide already, a quick refresher won’t hurt.
The entire point of assets:precompile
is to completely remove Rails from the equation when assets are served to browsers in production. Rails does all the hard work of compiling assets during this rake task, and stages the finished products in public/assets
where the web server can see them. When browsers request CSS, JavaScript and image assets, the web server does what it does best: it serves those static files directly. Rails won’t be consulted at all.
Furthermore, the production settings for Rails are configured in such a way that Rails cannot serve assets without a web server. Again, this in the best interest for performance. By disabling all of the asset pipeline-related code, Rails uses less memory and can process non-asset requests faster. But, it means that if you don’t precompile, or if you aren’t using a web server, your app simply won’t work.
What happens during assets:precompile
To summarize, rake assets:precompile
carries out these steps:
- Compile, merge and minify everything referenced in
application.js
andapplication.css
. Place the results inpublic/assets
. Image assets are copied over as well. - Generate a cache-buster based on each file’s MD5 checksum and put it in the filename: e.g.
application.css
becomesapplication-de9f…05b4.css
. - Create a gzipped version of each file: e.g.
application-de9f…05b4.css.gz
- Generate
public/assets/manifest-e4aa7…86f4.json
.
The manifest.json
is very important: it contains key/value mappings that match each asset name to its MD5 cache-busted name. This allows Rails to look up the cache-busted filename at runtime without recompiling or re-checksumming.
What happens when the browser requests a page
Okay, the assets are all precompiled, and the manifest.json
is ready to go. Your app is up and running, and a request for the home page arrives. Now what?
The browser makes the request for the home page:
GET /
The web server (let’s assume Nginx) hands off the request for
/
to your Rails app, where it routes to the appropriate controller, your application logic is invoked, and a view is rendered. So far this is straightforward Rails.Your view requests that Rails render a stylesheet
<link>
tag forapplication.css
like this:<%= stylesheet_link_tag "application" %>
Rails looks up
application.css
inpublic/assets/manifest-e4aa7…86f4.json
and finds the cache-busted filename. It renders:<link rel="stylesheet" href="/assets/application-de9f692fe73ec6663618e53a66d805b4.css"/>
The browser requests the stylesheet:
GET /assets/application-de9f692fe73ec6663618e53a66d805b4.css
Nginx looks in
public/assets
, findsapplication-de9f…05b4.css.gz
and serves that file directly. As is appropriate for cache-busted files, Nginx applies far-future expires headers to the response:Cache-Control: max-age=315360000, public Connection: close Content-Encoding: gzip Content-Length: 14496 Content-Type: text/css Date: Fri, 10 Feb 2012 05:09:40 GMT Expires: Thu, 31 Dec 2037 23:55:55 GMT Last-Modified: Tue, 07 Feb 2012 21:52:35 GMT Server: nginx/0.8.54
Note how Rails is bypassed completely when serving the CSS file.
Remember to configure Nginx
You’ll notice that Nginx has to do a lot of heavy lifting in order for the assets to be served correctly in production. And in fact, this behavior is not automatic. You need to explicitly configure Nginx to get the best results out of the asset pipeline in production.
Specifically you need to tell Nginx to do two things:
- Serve
public/assets
files with far-future expires headers - Use the precompiled
.gz
files
Refer to section 4.1.1 of the asset pipeline guide for specific instructions on properly configuring Nginx.
Continue reading part 3 of this series: Configuration.