Rails4 で 404 系のエラーページを制御する

Rails 4 では 404 系のエラー発生時にデフォルトで public/404.html を表示してくれるのですが、ヘッダーやフッターはエラーページでもそのまま利用したいケースなどでは自分で表示を制御する必要があります。
以下でその方法について記述します。

routes.rb の変更

まずは routes.rb を開いて、必ず一番下に次のようなルーティングを記述します。

get '*path', controller: 'application', action: 'render_404'

Rails 3 系までは match を使えば良かったのですが Rails 4 からは match は廃止されているため、わざわざ POST でアクセスしてくるケースにまで対応する必要はないと判断して、とりあえず get で記述する事にしました。

コントローラー側の変更

ルーティングは定義したので、コントローラーの変更を行います。今回は application_controller.rb を指定したので、ここに記述しましょう。

class ApplicationController < ActionController::Base
  # 略
  rescue_from ActionController::RoutingError, ActiveRecord::RecordNotFound, with: :render_404

  def render_404
    render file: "#{Rails.root}/public/404.html", layout: "not_found", status: 404
  end
end

ポイントは二つ、まずは rescue_from で例外を補足すること。これを行わないと例外が処理できずにデフォルトの 404.html を表示する動作が行われてしまいます。
次に render_404 アクションでレンダリングする際にステータスコードを 404 にする事。render メソッドで指定している status: 404 が該当箇所になります。
なお、今回は省きましたが 500 系の例外や他の 400 系の例外についても同様に処理ができるので必要に応じて追加、変更してください。

補足 ActiveRecord::RecordNotFound について

コントローラーで次のような記述をした場合

@hoge = Hoge.find(params[:id])

この時は該当する id を持つレコードが存在しなければ例外を出してくれるのですが、次のような記述の場合

@hoge = Hoge.where(id: params[:id]).first

これではレコードが存在しなかった時は単に nil になるだけで例外は発生しません。どうしてもこのような記述が必要であれば、次のように明示的に例外を出す事で 404 ページへの誘導が可能になります。

@hoge = Hoge.where(id: params[:id]).first
raise ActiveRecord::RecordNotFound if @hoge.blank?

追記

コメントにて指摘を受けたので、修正しておきます。
Rails 4 系では find_by 及びレコードが見つからなかった際に例外を出してくれる find_by! が存在するため、そちらを使う方が簡潔にコードが記述できます。

@hoge = Hoge.find_by(id: params[:id]) # この場合見つからないと nil が返る
@hoge = Hoge.find_by!(id: params[:id]) # この場合見つからないと例外が発生する