I ran across an issue in production recently where my Rails app assets weren’t being served correctly through the Asset Pipeline. It brought back to my attention the fact that I didn’t really understand how all of the Asset-Pipeline-related configuration settings work together.
I got so frustrated that I decided to write out a truth table and test out all possible combinations, and I’d like to share my findings. Hopefully this will help you better understand error messages you may receive, and maybe even show you a setup you’d like to use.
Here’s the tl;dr for if you’re getting a certain error:
- If you’re getting a 404 for assets at an
/assets/
path, make sure you haveconfig.serve_static_files = true
, or that you are running inside a web server that will serve these files. - If you’re getting a 404 for assets at a
/stylesheets/
or/javascripts/
paths, this is because you’ve set Rails not to compile assets at runtime (config.assets_compile = false
), and some other settings are incorrect. If you want assets to be precompiled, make sure all your settings match row 6 above.
Here are the settings I investigated:
config.assets.compile
: allows Rails to generate assets at runtime.config.assets.debug
: disables concatenating files, instead creating separate<link>
and<script>
tags for each.config.assets.digest
: appends fingerprints to asset URLs to bust cache.config.serve_static_files
: allows Rails to serve up files in/public/
, including precompiled assets.- Running
rake assets:precompile
: generates assets at build time and stores them in/public/
.
Here are the effects of enabling and disabling these settings in different combinations, as to whether the asset loads (HTTP status 200) or can’t be found (404):
# | compile | debug | digest | serve | precompile | status | path(s) |
---|---|---|---|---|---|---|---|
1 | true | true | true | (either) | (either) | 200 | /assets/jquery.self-FINGERPRINT.js?body=1 |
2 | true | true | false | (either) | (either) | 200 | /assets/jquery.self.js?body=1 |
3 | true | false | true | (either) | (either) | 200 | /assets/application-FINGERPRINT.js |
4 | true | false | false | (either) | (either) | 200 | /assets/application.js |
5 | false | true | (either) | (either) | (either) | 404 | /javascripts/application.js |
6 | false | false | true | true | true | 200 | /assets/application-FINGERPRINT.js |
7 | false | false | true | true | false | 404 | /javascripts/application.js |
8 | false | false | true | false | true | depends | /assets/application-FINGERPRINT.js |
9 | false | false | true | false | false | 404 | /javascripts/application.js |
10 | false | false | false | (either) | (either) | 404 | /javascripts/application.js |
A few observations:
- If you have
config.assets.compile = true
, every other combination of settings works.config.serve_static_files
andrake assets:precompile
don’t make a difference because the application will always fall back to compiling the assets on the fly. But you can changeconfig.assets.debug
andconfig.assets.digest
to control concatenation and fingerprinting, respectively. - If you have
config.assets.compile = false
, there are only two combinations of settings that work.config.assets.debug
must be set tofalse
,config.assets.digest
must be set totrue
, and you must runrake assets:precompile
. Your setting forconfig.serve_static_files
is the only one that can vary, and the setting you want depends on how you’re running Rails. If it’s running behind a web server like Apache or Nginx, you can setconfig.serve_static_files
to false, because the web server will serve up those files. If Rails is not running behind a web server, you will need to setconfig.serve_static_files
to true, so that Rails will serve these files. - Note that Heroku precompiles your assets if it doesn’t already see the compiled assets committed to Git. If you move off of Heroku to another host, you may need to start precompiling yourself.
If you’ve seen the Asset Pipeline behave differently or there’s another setting I should be testing against, let me know!