Rails: Custom 404 Pages

05 Aug 2006

I launched JCFootballProspects.com a while ago and things have been going smoothly but the site is stuck with the nasty black-on-white error messages when somebody types in a wrong address.

I’m now at the point where I want to give out some fancy 404 pages and I’ve been looking around at what other folks have done. I’m amazed to find that all the examples I can find have a 404.html page in the #{RAILS_ROOT}/public directory. I’m sure it’s effective to just have a plain-html file that gets served up for errors (and it’s certainly a lot better than the default ugly stuff) but I’m convinced there are advantages to having application-provided 404 pages as well.

There are three different kinds of errors I trap and respond to:

  • A user gives incorrect or insufficient parameters to a page view
  • A user attempts to access an action that doesn’t exist
  • A user attempts to access a controller that doesn’t exist

The first is easily handled - even the AWDWR book shows in an early example that you can output a flash message if something goes wrong with a request. The second is only slightly more complicated but, luckily, Ruby has a great way to respond to missing actions on controllers using method_missing. The solution to the third is provided by Rails’ routing capabilities.

How to hook up an app-driven 404 page:

The first thing I did was added a view to my default controller (mine is called ‘home’). I created ./app/views/home/404.rhtml and put just some basic stuff in it:

    <%= content_tag 'h2', 'Whoops!' %>

    <%= content_tag 'h3', 'Page not found' %>

    <p>[insert message here]</p>

The next step was to add method_missing to my application controller:

    class ApplicationController < ActionController::Base


      def method_missing(methodname, *args)
       @methodname = methodname
       @args = args
       render 'home/404', :status => 404


And one super low priority route (put it at the very bottom) finishes off the job:

    map.error ':controllername',  :controller => 'home',
                                  :action => '404'
  • sasha said: there is another more generic way that will catch all the errors, you can inspect the exception to route to the right view, and possibly log the trace: in application.rb
    def rescue_action_in_public(exception)
      # do something based on exception
      message = exception.backtrace.join("\n") unless exception
      render :file => "#{RAILS_ROOT}/public/404.html", :layout => false, :status => 404
    def local_request?
    thanks for the CSV tip. doc is atrocious
  • Danger said: That's brilliant. I'd been wondering where the default rescue page was coming from. I won't bother to edit this post to reflect your mad new style but I hope everybody reads your comment.
  • Ben said: Surely you can just change the error documents in /Public/...?
  • Danger said: It's true - you can just change 400.html to read what you want. But this allows you to customize the layout. You can still show the person logged in at the top-right of the screen (if that's your thing) along with any other session vars, you can keep your layout, and (should you be super-tricky) you can customize the message based on what they were looking for. But you're right - 400.html is fine for a lot of folks.
  • Chris said: The problem with using a route like "map.error ':controllername'" is that it won't catch 'mysite.com/jibberish/morejibberish'. For a brickwall catch-all route, you're best off using a wildcard route: "map.error '*url' :controller => 'home' :action => '404'"
  • Anthony said: sorry, i don't think i quite get this. So basically, i did the changes to my application controller and added the view, modified the routes, but when i type in some rndom non-existing url, i get an error telling me that 'You called render with invalid options : application/404' (in my case, i don't use "home", but application) ny idea what exactly this means? A
  • Jack Danger said: Anthony: Try "render :template => 'application/404'" This article badly needs to be updated.
  • Tony Mcintyre said: teraglin nizam rendlewood reorient unenraged phonautographically reservery sulphocarbamic Plymouth Creek Christian Church http://www.mnpro.com/
  • sweetperceptions said: What about custom 500 pages? it surely won't be caught by the low priority route entry.. what are your thoughts?
  • sweetperceptions said: I just found out how to catch the 500 error messages. You can read it here: http://coderkitty.sweetperceptions.com/2008/7/6/meaningful-404s-and-500s

Please if you found this post helpful or have questions.