Railsのエラー画面を静的なものにするか動的名ものにするかという話は、よく上がるかと思います。 Assetsを利用したいし、他言語化したいし、エラーコードでメッセージだけ出しわけしたいし、 というので動的エラー画面を選択することがあるかと思います。
ちょっと待って
結論から書くと、いろいろ調べるとできなくはないが、問題が発生しうるケースがあるので
可能であれば、静的で行く方が良いようです。
どうしてもという場合は、yuki24/rambulance を使いましょう。
そもそも503考慮するなら静的ページ用意しなくちゃならないですしね。
昔話
詳しく知りませんでしたが、3.2ぐらいからRailsの動的エラー画面の生成方法が変わっていたようです。 2.3時代は、rescure_action_in_public を利用していました。 RailsCasts懐かしい。
上記RailsCastsは、Pro(有料)版でrevisedされているようです。
#53 Handling Exceptions (revised) - RailsCasts
githubのソースを見ると、self.routes を突っ込むタイプのようでした。 この方法は、かつて Edge RailsGuides に載っていたらしいのですが(今は載ってない)、 問題があるようです(後述)。
config.exceptions_app = self.routes
動的エラーについて
ググると rescue_from 方式がたくさんヒットします。
確か rescue_from って問題あったんじゃなかったけっと、さらに調べると
yuki24 さんのコメントを見つけました。
qiitaではこの手の話題に対して、いろんなところでフォローしてくれているようでよく見かけました。
Railsの404,500エラーページをカスタマイズ - Qiita
ActionController::RoutingError は ApplicationController に 到達する前に投げられる例外なので、 rescue_from ActionController::RoutingError, ... は期待した通りに動かない (get '*path', to: ... によって、動いているように見えているだけ)
なるほどそういうことなんですね。
ApplicationController で rescue_from Exception, ... すると Airbrake や Bugsnag なんかが動かなくなる。
これはよくわからないので調べます(後述)。
では、どうするのがよいかというと、Rails には config.exceptions_app という設定があり、 ここに Rack mountable なオブジェクトを与えることで動的なエラーページを生成可能になります。 とはいえ、エラーページのためだけにもうひとつ Rack アプリを作るのも面倒なので、 とりあえず ErrorsController というのを作って、lambda で囲って渡しちゃえば動きます。
config.exceptions_app = ->(env) { ErrorsController.action(:show).call(env) }
なるほど、そんなことになってたんですね。
もう少し調べてみる
以下が非常に参考になりました。
Rails の rescue_from で拾えない例外を exceptions_app で処理する - Qiita
rescue_from の問題点、exceptions_app でも完璧ではない点。
Railsアプリの例外ハンドリングとエラーページの表示についてまとめてみた - Qiita
ActionDispatch::PublicExceptions、NewRelic, Airbrake などは Rack Middleware
に差し込むタイプのgemなので、使えなくなる。
Railsでエラーページを動的に - Qiita
config.exceptions_app = routes
の問題点と rambulance について記載があります。
動的ページの問題を解決するために作成したとのこと。
でも、静的ページを用意するためのあくまでつなぎとして使って欲しいとのこと。