Precompiled Rails Static 404 and 500 Pages
Great looking custom 404 and 500 pages are a hallmark of well-rounded and polished web applications. Everyone has seen Github’s excellent ones, but check out this sweetness designed by the talented @kolber for the recently relaunched The Thousands, for which we built a custom Rails CMS.
There was a really handy trick we used for these static error pages in Rails. We compiled them by augmenting the rake assets:precompile
task. This has many benefits. Firstly, it gives you access to digest paths for assets allowing you to use all the asset_tag
related helpers in your templates (“templates” you say? We’ll get to that). Secondly, it helps keep these pages looking fresh as they’ll always have the latest CSS, and if you ever reference an asset that no longer exists in the manifest then the precompile task will fail, thus alerting you to the problem that you’d otherwise never notice.
Setting this up is super simple. First you need to create an app/assets/html
directory. Then you need a 404.html.erb
and a 500.html.erb
(only ERB can work). Then a template might look something like this:
<!DOCTYPE html>
<html class="no-js" lang="en">
<head>
<meta charset="utf-8">
<title><%= ENV['APP_NAME'] %> 404</title>
<%= javascript_include_tag 'modernizr' %>
<%= stylesheet_link_tag 'application', :media => 'all', :digest => true %>
</head>
<body>
<section id="error">
<h1>404 Page not found</h1>
<a href="/" class="return">← Return to Homepage</a>
</section>
<script>
Modernizr.load("#{javascript_path 'application'}");
</script>
</body>
</html>
You notice the asset_tag
related helpers all work, but you do however require the :digest => true
option on stylesheet link tags.
Now, for the pièce de résistance, the rake task that compiles the templates into your Rails public
directory. Throw this in lib/tasks
:
require 'fileutils'
Rake::Task['assets:precompile'].enhance do
Rake::Task['assets:precompile_static_html'].invoke
end
namespace :assets do
desc 'Compile the static 404 and 500 html template with the asset paths.'
task :precompile_static_html do
invoke_or_reboot_rake_task 'assets:precompile_static_html:all'
end
namespace :precompile_static_html do
def internal_precompile_static_html
# Ensure that action view is loaded and the appropriate
# sprockets hooks get executed
_ = ActionView::Base
config = Rails.application.config
config.assets.compile = true
config.assets.digest = true
env = Rails.application.assets
target = Rails.public_path
compiler = Sprockets::StaticCompiler.new(
env,
target,
['404.html', '500.html'],
:manifest_path => config.assets.manifest,
:digest => false,
:manifest => false
)
compiler.compile
end
task :all do
ruby_rake_task('assets:precompile_static_html:primary', false)
end
task :primary => ['assets:environment', 'tmp:cache:clear'] do
internal_precompile_static_html
end
end
end
This will magically get called whenever you rake assets:precompile
. You just need to add the following line of config to your application.rb:
config.assets.paths << "#{Rails.root}/app/assets/html"
One side effect to this; as well as being copied to your public directory, the compiled templates will also exist in your assets directory (in our case that’s S3). This isn’t the end of the world though and if you’re using Heroku you can reuse them as your Heroku platform error pages.
If you liked this article then you might also like my previous one on organising the Rails asset pipeline.