Nginxカスタムエラーページ:ユーザー体験の向上
404、403、50x応答に対して便利なNginxカスタムエラーページを設定し、実際の障害を隠さない方法。
Nginxカスタムエラーページ:ユーザー体験の向上
Nginxカスタムエラーページは、生の障害を明確な次のステップに変えます。壊れたリンク、欠落したファイル、クラッシュしたアップストリームアプリを修正するわけではありません。それらは小さいながらも価値のあることを行います:訪問者に何が起こったかを平易な言葉で伝え、サイトから追い出された感覚を与えないようにします。
これは通常のミスで重要です。誰かが古いドキュメントリンクをたどって404を取得する。プライベートファイルパスが403を返す。デプロイ中にアプリが再起動し、Nginxが一時的に502を表示する。カスタムページがない場合、ユーザーは突然または技術的に見えるデフォルトのサーバー応答を見るかもしれません。適切な静的ページがあれば、検索、戻る、再試行、待機のどれをすべきかがわかります。
最高のエラーページは退屈な運用ツールです。静的で、高速で、アクセス可能で、正直です。監視から障害を隠さず、内部詳細をユーザーに公開しません。
人々が実際に見るエラーから始める
すべてのHTTPステータスコードに対してページをデザインする必要はありません。一般的なものから始めましょう。
404 Not Foundは最初にカスタマイズするページです。要求されたURLがファイル、ルート、またはコンテンツを返すNginxロケーションと一致しない場合に表示されます。古いリンク、名前変更された投稿、削除されたドキュメントページ、手入力されたURLがすべてここにつながります。
役立つ404ページは次のように言います:「そのページが見つかりませんでした。」次に、ホームページ、ドキュメントインデックス、製品エリア、または検索ページへのパスを提供します。ユーザーを責めないでください。URLは彼らがクリックするずっと前に間違っていた可能性があります。
403 Forbiddenは異なります。Nginxはリクエストを理解しましたが、提供しません。原因には、ファイル権限、アクセスルール、ディレクトリリスティングの無効化、IP許可/拒否ルール、または認証要件が含まれます。403ページは落ち着いて短くする必要があります。リソースがプライベートな場合は、そう伝えてください。ユーザーがアクセスを必要とする可能性がある場合は、適切なログインまたはサポートパスを指し示します。
アプリベースのサイトでは、50xエラーを慎重に処理します:
500 Internal Server Errorは通常、リクエスト処理中にアプリケーションが失敗したことを意味します。502 Bad Gatewayは多くの場合、Nginxがアップストリームサービスから有効な応答を受信しなかったことを意味します。503 Service Unavailableはメンテナンス、過負荷、または意図的に利用不可にされたサービスに役立ちます。504 Gateway TimeoutはNginxがアップストリーム応答を待ちすぎたことを意味します。
SaaSダッシュボードの例:デプロイ中にアプリプロセスが再起動します。数秒間、Nginxはアップストリームに接続できず、ユーザーは502を表示します。カスタムページは「ダッシュボードは一時的に利用できません。1分後に更新してください。」と言うことができます。完璧ではありませんが、デフォルトのゲートウェイエラーよりは明確です。
静的エラーファイルを作成する
エラーページをアプリケーション依存関係チェーンの外に保ちます。データベースがダウンしていても、500ページはロードされるべきです。Node、Python、Ruby、またはPHPアプリが正常でなくても、Nginxは静的フォールバックを提供するべきです。
シンプルなファイルレイアウトは次のようになります:
/var/www/example.com/public/
index.html
assets/
/var/www/example.com/errors/
404.html
403.html
50x.html
HTMLは軽量に保ちます。サードパーティのスクリプト、重い画像、クライアントサイドアプリバンドル、壊れたバックエンドを呼び出すものは避けてください。Nginxが直接提供できる小さなCSSファイルは問題ありません。
最小限の404.htmlは次のようになります:
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ページが見つかりません</title>
</head>
<body>
<main>
<h1>ページが見つかりません</h1>
<p>ページが移動したか、リンクが古くなっている可能性があります。</p>
<p><a href="/">ホームページに戻る</a></p>
</main>
</body>
</html>
それで十分です。サイトに合わせてスタイルを設定できますが、メッセージとリンクが装飾よりも重要です。
error_pageでページを配線する
Nginxでは、error_pageディレクティブが1つ以上のステータスコードをURIにマッピングします。基本的なサーバーブロックは次のようになります:
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com/public;
error_page 404 /errors/404.html;
error_page 403 /errors/403.html;
error_page 500 502 503 504 /errors/50x.html;
location /errors/ {
internal;
root /var/www/example.com;
}
location / {
try_files $uri $uri/ =404;
}
}
パス解決は人々がつまずく部分です。この例では、/errors/以下のリクエストはroot /var/www/example.com;を使用するため、/errors/404.htmlは/var/www/example.com/errors/404.htmlにマッピングされます。
internalディレクティブは、外部クライアントが通常のURLとして直接/errors/404.htmlを要求できないことを意味します。Nginxはエラー処理時に内部的にそれを提供できます。
設定を編集した後、テストしてリロードします:
sudo nginx -t
sudo systemctl reload nginx
次に動作をテストします:
curl -I http://example.com/definitely-missing-page
curl -s http://example.com/definitely-missing-page | head
ステータスは依然として404であるべきで、ボディはフレンドリーなHTMLですが。よくある間違いは、欠落ページに対して誤って200 OKを返すことであり、これにより監視や検索エンジンが欠落URLを実際のページと誤解します。
リバースプロキシ背後でのカスタムページ
Nginxがアップストリームアプリケーションにプロキシする場合、アプリは独自のエラー応答を返す可能性があります。デフォルトでは、プロキシされた応答は通常クライアントにそのまま渡されます。Nginxがアップストリームエラー応答をインターセプトし、error_pageルールを使用するようにするには、関連コンテキストでproxy_intercept_errorsを有効にします。
location /app/ {
proxy_pass http://app_backend;
proxy_intercept_errors on;
error_page 502 503 504 /errors/50x.html;
}
Nginxドキュメントは、proxy_intercept_errorsがステータスコード300以上のプロキシ応答に適用され、その後error_page処理のためにNginxにリダイレクトできると説明しています。実際には、考えずにどこでも有効にしないでください。
ブラウザページの場合、502または503をインターセプトすることはしばしば有用です。JSON APIの場合、間違っている可能性があります。APIクライアントは通常、HTMLページではなく構造化されたJSONエラーボディを期待します。別々のロケーションが必要かもしれません:
location /api/ {
proxy_pass http://api_backend;
proxy_intercept_errors off;
}
location /dashboard/ {
proxy_pass http://app_backend;
proxy_intercept_errors on;
error_page 502 503 504 /errors/50x.html;
}
この分割により、人間向けページはフレンドリーに保ち、機械可読なAPIエラーは維持されます。
正しいステータスコードを保持する
Nginxではerror_pageで応答コードを変更できますが、意図がある場合のみ行ってください。これは有効な構文です:
error_page 404 =200 /fallback.html;
ほとんどのウェブサイトでは、それは悪い考えでしょう。欠落ページは404のままであるべきです。検索エンジン、アップタイムチェック、分析、ユーザーすべてが真実から利益を得ます。
コードを変更する正当なケースもあります。特定のエラーを名前付きロケーションにルーティングしたり、503でメンテナンスページを返したりする場合です。しかし、デフォルトルールとして、元のエラーステータスを保持してください。
メンテナンスの場合、明示的にできます:
location / {
return 503;
}
error_page 503 /errors/maintenance.html;
location = /errors/maintenance.html {
root /var/www/example.com;
internal;
}
Nginxの前にCDNやロードバランサーを使用している場合、それらが独自のエラーページ動作を持つ可能性があることを覚えておいてください。どのレイヤーがどのエラーを所有するかを決定してください。そうしないと、Nginxを直接テストして1つのページを見る一方、CDN背後にいるユーザーは別のページを見るかもしれません。
人間向けのエラーページを書く
コンテンツは3つの質問に迅速に答えるべきです:
- 何が起こったのか?
- 一時的なものか?
- 次に何ができるか?
404の場合、有用な次のステップは検索、ホームページ、ドキュメントインデックス、または欠落ページが存在すべき場合はサポートへの連絡です。503の場合、後で再試行するかステータスページを確認するという有用なガイダンスです。403の場合、適切であればサインインまたはアクセス要求の指示を指し示します。
スタックトレース、アップストリームホスト名、ファイルシステムパス、パッケージバージョン、内部IP、説明のないリクエストID、インシデント詳細を避けてください。リクエストIDはサポートが使用できる場合に有用ですが、明確にラベル付けしてください:
<p>サポートに連絡する場合、このリクエストIDを含めてください:<code>$request_id</code></p>
$request_idのような変数を注入するには、それをサポートする設定パターンが必要です。静的HTMLファイルはそれ自体でNginx変数を展開しません。多くのチームは公開エラーページを静的のままにし、代わりにログに依存してリクエストIDを取得します。
アクセシビリティは有用性の一部です。明確なh1、読みやすいコントラスト、通常のリンク、プレーンテキストを使用してください。唯一の回復アクションを小さなアイコンやスクリプト駆動のボタンにしないでください。
意図的にページをテストする
ユーザーがエラーページを見つけるのを待たないでください。変更のたびにテストしてください。
404の場合:
curl -i https://example.com/no-such-page
403の場合、制御されたテストロケーションを作成するか、プライベートテストファイルを使用します。エラーをトリガーするためだけに本番権限を緩めないでください。
502または503の場合、利用不可のアップストリームを指すロケーションを使用してステージングでテストします:
location /broken-upstream-test/ {
proxy_pass http://127.0.0.1:59999;
proxy_intercept_errors on;
error_page 502 503 504 /errors/50x.html;
}
次にそれをリクエストし、ステータスコードとボディの両方を確認します:
curl -i https://staging.example.com/broken-upstream-test/
また、ログを監視します:
sudo tail -f /var/log/nginx/error.log /var/log/nginx/access.log
見栄えの良いエラーページは運用シグナルを消してはいけません。50xレートが上昇したときにアラートが発火するべきです。
Nginxカスタムエラーページは小さな設定タスクでありながら、実際のユーザー影響があります。404、403、および一般的な50xエラーから始めてください。Nginxから直接静的ファイルを提供します。正確なステータスコードを保持します。HTMLフォールバックが意味をなす場所でのみproxy_intercept_errorsを使用します。その後、他の本番動作と同じ方法でページをテストします。