Showing visitors a bare default page when something breaks is a poor look. Rails custom error pages fix that: point exceptions_app at your routes, then render 404 and 500 views from a controller.
This guide belongs to the wider topic of ruby on rails debugging.
| Item | Value |
|---|---|
| Default static pages | public/404.html and public/500.html |
| Setting that hands errors to your routes | config.exceptions_app = routes |
| Setting to preview custom pages in development | config.consider_all_requests_local = false |
| Route option that covers any HTTP method | via: :all |
| Static pages to delete after switching | 404.html, 422.html and 500.html |
These values come from a Reintech guide and a makandra dev card that was last updated on 2025-08-18. Rails settings and defaults can change between versions, so check the sources before you rely on them.
Why do the default static pages fall short?
By default, Rails serves static HTML files from the public directory (404.html, 500.html), as the Reintech guide explains. Those pages cannot reach your application layout, helpers or dynamic content.
The same guide warns that default Rails error pages expose technical details and break your application’s visual consistency. The makandra card makes the same point: you can restyle the static files, but you cannot use Haml, Rails helpers or your application layout in them.
The fix is to let a controller render the page. Rails hands failed requests to a Rack app called the exceptions app, and you can replace it with your own routes.
The Gaffe gem README says this exceptions_app feature has been present since Rails 3.2, so the approach below is not new.
Wiring Rails to your own error routes
The setup takes a handful of small edits. Follow them in this order.
- Add config.exceptions_app = routes to config/application.rb, which registers your own app as the one responsible for showing error pages.
- Append error routes to config/routes.rb, for example: match ‘/404’ => ‘errors#not_found’, via: :all, and the same pattern for ‘/422’ and ‘/500’.
- Add an ErrorsController and a view for each action.
- Delete the static error pages 404.html, 422.html and 500.html from public/.
All four steps follow the makandra card. The Reintech guide adds that via: :all makes error pages work for any HTTP method (GET, POST, PUT, etc.), which matters because exceptions can occur during any type of request.

Building the controller and views
A minimal controller needs only two actions. In the makandra example, not_found renders with status 404 and server_error renders with status 500, both limited to the HTML format.
The Reintech guide extends this with a before_action that reads the original error from request.env[‘action_dispatch.exception’]. It lists exception logging for production debugging as one benefit of that version.
Keep the views simple and useful:
- On the 404 page, say the page does not exist or has moved, and link back to the home page.
- On the 500 page, provide reassurance without exposing technical details.
- Show stack traces only in development, never to production visitors.
Two constraints are worth knowing before you add features. The makandra card notes that you cannot modify headers on these pages, so you cannot touch the session or cookies. The Gaffe README suggests getting rid of filters, such as authentication, so that error pages are always accessible.
The Reintech guide also suggests pairing the pages with an error tracking service like Sentry or Rollbar. It shows an error reference number on the 500 page, which helps support teams correlate user reports with server logs.
Previewing and testing the pages
In development, Rails shows its own debug page, so your views never appear. Per the makandra card, you only see the custom page if you set config.consider_all_requests_local = false in config/development.rb. The weLaika post from 2015 says to set it temporarily, then switch it back.
For automated checks, write request tests rather than controller tests. The Gaffe README explains that controller tests only test method calls in the controller class and do not go through the entire Rack stack to simulate a real HTTP request.
In a request spec, the makandra card shows how to make Rails render exception templates instead of raising exceptions. Check both the status code and the page content, and confirm your application layout appears.
Handling missing records and unexpected failures
A custom 404 page renders only when Rails cannot find a match in config/routes.rb, according to the weLaika post. That covers mistyped URLs, but it does not cover a record that goes missing inside a controller action.
For an exception raised inside a controller, the same post suggests a rescue_from handler for ActiveRecord::RecordNotFound. The handler is a protected method in ApplicationController that renders the 404 view with the not_found status. Keeping that status in place means the response still reports a missing page instead of a success.
The post also warns that the order of multiple rescue_from conditions matters. The guide to rails rescue_from covers that pattern in more depth.
Rails also picks a particular error page automatically for some types of unhandled exceptions, as the makandra card notes. If your application or a gem raises its own exception, the Gaffe README says you can map it to a specific rescue response so the matching view renders.
For each exception you handle, settle three questions before you write the handler:
- Which status the visitor should see, such as a missing page, a forbidden request or a server error.
- Which view renders for that status, and whether it uses your application layout.
- Whether the exception is logged, so you can debug it later.
Trigger each handled exception in a request test, so the page and its status are verified together. That catches a handler that renders the right view with the wrong status.
It also helps to keep the handlers in one place, such as ApplicationController, so that every controller inherits the same behavior. A single location makes it easier to see which exceptions are covered and which still fall through to the generic server error page.
When a server error page appears and you need the cause, the guide to rails 500 internal server error walks through tracing it. Your error page should hide the details from visitors, but your logs should still capture them.
Frequently asked questions
Can I just edit the static files in public?
Show answer
Yes. The makandra card says you can add basic styles by editing templates such as public/404.html. Those files are static, so you cannot use Rails helpers or your application layout in them.
Why do I still see the Rails debug page in development?
Show answer
Development shows detailed errors by default. Set config.consider_all_requests_local = false in config/environments/development.rb to preview your pages, as the weLaika post describes.
Will my error notification library still work?
Show answer
The weLaika post from 2015 reports that Airbrake with Errbit kept working with custom error pages and needed no extra work. Test your own tool, since versions and setups differ.
Is there a gem for this?
Show answer
Gaffe is one example. Its README says it uses exceptions_app and comes with default error pages that are easy to override. A gem is optional, because the manual setup above needs only a controller, routes and views.



