503 Service Unavailable:ただの「エラー」で終わらせない、プロの礼儀作法
Webインフラの現場にいると、必ず一度は対峙するのが「503 Service Unavailable」だ。ロードバランサーがバックエンドの沈黙を検知し、あるいはメンテナンスの切り替えタイミングで、クライアントに「今は無理だ」と伝える。
新人エンジニアはここで「あぁ、サーバーが死んでいるんだな」とログを眺めて終わりにするかもしれない。しかし、熟練のエンジニアは違う。我々が考えるべきは、「いかにしてクライアントの無駄な再試行を抑制し、いかにしてサービス復旧をスマートに伝えるか」という、いわば通信の作法だ。
今回は、HTTP/1.1の時代から続く堅牢な通信制御の要、「Retry-After」ヘッダーの正しい扱い方について、現場の知見を交えて深掘りしていく。
—
503エラーの正体と「Retry-After」の役割
HTTP 503は「サーバーが現在、リクエストを処理できない」ことを示す。RFC 7231で定義されている通り、これは一時的な状態だ。
ここで重要なのが、`Retry-After`ヘッダーである。これがあるか無いかで、クライアント側の挙動は劇的に変わる。無策に503を返し続ければ、クライアントは「とりあえずすぐリトライ」を繰り返し、復旧しかけたサーバーを即座に再ダウンさせる――いわゆる「リトライストーム」という地獄を生むことになる。
Retry-Afterの2つの指定方法
RFCによれば、以下のいずれかの形式で時間を伝達する。
1. 秒数(HTTP-date形式ではなく整数): 「あと何秒待てばいいか」をシンプルに伝える。推奨される実装だ。
2. HTTP-date形式: 「いつ復旧するか」という絶対時刻を指定する。
例:あと60秒待ってね
Retry-After: 60
例:2023年12月31日の23:59:59まで待ってね
Retry-After: Sun, 31 Dec 2023 23:59:59 GMT
—
実践:アプリケーションとインフラでの実装
では、実際にどう実装すべきか。Nginxでのハンドリングから、クライアント側の検知までを見ていこう。
1. Nginx側での設定(インフラ層)
メンテナンス中や、upstreamの全滅時に静的に返す設定だ。
nginx.conf の設定例
location / {
# 503を返す設定
return 503;
# クライアントに300秒(5分)の待機を要求
add_header Retry-After 300 always;
}
※ `always`をつけるのが肝だ。エラーページを表示する際にもヘッダーが確実に付与される。
2. Python (Requests) での検知
APIクライアントを書く際、503が返ってきたら愚直にループさせるのではなく、ヘッダーを解釈して `sleep` するのが一流のやり方だ。
import requests
import time
url = “https://api.example.com/data”
response = requests.get(url)
if response.status_code == 503:
# Retry-Afterヘッダーがあれば取得、なければデフォルトで60秒待機
wait_time = int(response.headers.get(“Retry-After”, 60))
print(f”サーバー混雑中。{wait_time}秒後に再試行します…”)
time.sleep(wait_time)
# 再度リクエストを実行するロジックへ
3. JavaScript (Fetch API) での検知
モダンなWebアプリでも同じだ。
fetch(‘https://api.example.com/data’)
.then(response => {
if (response.status === 503) {
const retryAfter = response.headers.get(‘Retry-After’) || ’60’;
console.log(`サーバー負荷大。${retryAfter}秒待機します`);
// この値を使ってsetTimeoutでリトライをスケジューリングする
}
});
—
現場で役立つデバッグTips
最後に、トラブルシューティング時の注意点をいくつか挙げておく。
- クライアントの行儀の悪さ: どんなに丁寧に `Retry-After` を返しても、それを無視して1秒おきに叩き続けてくる行儀の悪いライブラリは存在する。サーバーサイドでレートリミットをかける際、`Retry-After` を守っているクライアントをホワイトリストに入れるような「優しさ」ある設計も、時には必要だ。
- ロードバランサーの挙動に注意: AWSのALBやNGINXがエラーを生成する場合、独自のヘッダーを付与することがある。`Retry-After` が上書きされていないか、`curl -I` で必ず確認してほしい。
- 負荷の可視化: 503を返した数と、その後のクライアントのリトライ回数をモニタリングすることで、システムが「許容できる負荷の限界」を正確に把握できるようになる。
まとめ:通信は「対話」である
503は単なるエラーコードではない。それはサーバーからの「少し休憩が必要だ、少し時間を置いてからまた来てほしい」という誠実なメッセージだ。
このメッセージを適切に設計し、クライアント側で正しく解釈する。この地味な積み重ねこそが、大規模トラフィックを捌く堅牢なシステムを支える土台となる。教科書的な知識で終わらせず、ぜひ次のリリース時のヘッダー設計に活かしてほしい。
何かあれば、またいつでも聞いてくれ。現場からは以上だ。
コメント