こんにちは!ネットワークやAPIの世界へようこそ。インフラアーキテクトの私です。
Webアプリケーションを作ったり、便利なAPIを叩いたりしていると、時々「おいおい、ちょっとアクセスが早すぎるよ!」とサーバーからピシャリと門前払いされてしまうことがありますよね。
今回は、そんな時に登場するお馴染みのエラー、HTTPステータスコード 429 (Too Many Requests) と、サーバーからの優しい(?)伝言である Retry-After ヘッダー について、現実世界の仕組みに例えながら、一歩ずつ優しく紐解いていきたいと思います。
難しい用語が出てきても慌てなくて大丈夫です。一緒にじっくり見ていきましょう!
—
1. なぜ「429エラー」は起きるのか?(郵便配達の例え)
皆さんは、人気のラーメン店や、チケットの発売日にスマホの更新ボタンを連打した経験はありませんか?あんな風に、短時間にものすごい数のリクエストをサーバーに送りつけると、サーバーはこう悲鳴を上げます。
> 「ちょっと待って!私一人しかいないんだから、そんなに一気に話しかけられても処理しきれないよ!」
これが、レートリミット(回数制限)を超えた瞬間です。サーバーがこれ以上パンクしないように、自らを守るための防衛システムが働いているんですね。
これを現実世界に例えるなら、「人気の役所の窓口」に似ています。
窓口の担当者が一人しかいないのに、あなたが毎秒「住民票をください!」「印鑑証明を!」と何十回も書類を差し出してきたら、担当者はどうするでしょうか?
「お客様、ルールとして一度に受け付けられるのは1分間に1回までです。今は列に並んで順番を待ってくださいね」と、窓口でピシャリと追い返されますよね。この「追い返された状態」こそが、Webの世界における 429 Too Many Requests なのです。
—
2. サーバーからの優しいアドバイス:Retry-After ヘッダー
さて、役所の窓口で「出直してきなさい!」と言われたとします。ここで私たちが知りたいのは、「一体、何分後にまた来ればいいの?」ということですよね。
もし「いつ来ていいか分からないから、とりあえず1秒おきに再挑戦(リトライ)しよう!」なんてプログラムを組んでしまったらどうなるでしょう? サーバーはまた同じ人から猛烈なアタックを受けることになり、いつまで経っても負荷が減りません。これではDDoS攻撃(サーバーをダウンさせるサイバー攻撃)と変わらない迷惑行為になってしまいます。
そこで登場するのが、Retry-After ヘッダーです。
サーバーが 429 のステータスコードを返すとき、一緒に「〇秒後ならまた来ていいよ」あるいは「〇月〇日の〇時〇分以降に来てね」というメッセージ(ヘッダー情報)をそっと添えてくれるのです。
これが、クライアント(アプリやブラウザ)にとっての「お利口さんに待つための羅針盤」になります。
Retry-After の2つの書き方
実際のネットワークの世界では、この「待つ時間」を伝えるために主に2つの形式が使われます。
1. 秒数で指定するパターン
- 例:
Retry-After: 60(「60秒間、出入り口で待ちなさい」という意味です)
2. 具体的な日時で指定するパターン
- 例:
Retry-After: Wed, 21 Oct 2028 07:28:00 GMT(「この時間になったらまたおいで」という意味です)
インフラの現場では、シンプルで計算しやすい「秒数指定」がよく使われます。
—
3. クライアント側の実装:行儀の良いプログラムを書こう
では、私たちがAPIを呼び出すプログラム(今回は例としてPythonを使ってみましょう)を書くとき、この 429 と Retry-After にどう向き合えばよいでしょうか。
「エラーが出たら即座に諦める」のではなく、「お行儀よく待ってからもう一度トライする(リトライ処理)」のがプロの作法です。実際のコードを見てみましょう。
import time
import requests
# データの取得先APIエンドポイント
url = "https://api.example.com/v1/data"
def fetch_data_with_retry():
max_retries = 3 # 最大で再挑戦する回数
attempt = 0
while attempt < max_retries:
# APIにリクエストを送信
response = requests.get(url)
# 正常に取得できた場合
if response.status_code == 200:
print("データの取得に成功しました!")
return response.json()
# レートリミット(429エラー)に引っかかった場合
elif response.status_code == 429:
attempt += 1
# サーバーから「Retry-After」ヘッダーが届いているか確認する
# ヘッダーがない場合のデフォルトとして「5秒」を設定しておく
wait_time = int(response.headers.get("Retry-After", 5))
print(f"制限に達しました (429)。サーバーの指示通り {wait_time} 秒間待機して再挑戦します... (試行回数: {attempt})")
# 指定された秒数だけプログラムの処理を一時停止する
time.sleep(wait_time)
else:
# 429以外の予期せぬエラーの場合
print(f"予期せぬエラーが発生しました: {response.status_code}")
break
print("最大試行回数を超えたため、処理を中断します。")
# 関数の実行
fetch_data_with_retry()
このコードのポイントは、response.headers.get("Retry-After", 5) という部分です。
サーバーが「〇秒待ってね」と言っていればその素直に従い、もしサーバーが言い忘れていたとしても「とりあえず5秒は待とう」というセーフティネット(フォールバック)を設けています。これが、ネットワークに優しい美しいプログラムの設計です。
—
4. サーバー・インフラ側の設定:Nginxでのレートリミット例
今度は逆に、あなたがAPIサーバーを守る「インフラエンジニア」の立場になったときの仕組みを少しだけ覗いてみましょう。
よく使われるWebサーバーの「Nginx(エンジンクス)」では、次のような設定で「1つのIPアドレスからのアクセスは1秒に1回まで!」といった制限を簡単にかけることができます。
# /etc/nginx/nginx.conf などの設定ファイル
# 1秒あたり1リクエストまでを許可するゾーンを定義(IPアドレスごとに管理)
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=1r/s;
server {
listen 80;
server_name api.example.com;
location /v1/ {
# 上で定義したゾーンを適用。
# burst=5 は「ちょっとしたバースト(瞬間的な連打)なら5回まで一時的に受け付けるよ」というゆとりを持たせる設定
# nodelay は、バースト分のリクエストを遅延させずに即座に裁く(あるいは429を返す)ためのスイッチ
limit_req zone=api_limit burst=5 nodelay;
# 通常のバックエンドサーバーへの転送設定
proxy_pass http://backend_cluster;
}
}
Nginxなどの優れたプロキシサーバーは、この制限を超えたアクセスに対して自動的に 429 Too Many Requests を返してくれます。インフラ側でしっかりと門番を立てておくことで、背後にある大切なデータベースやアプリケーションサーバーが過負荷でダウンするのを防いでいるのです。
—
まとめ
今回は、HTTPステータスコード 429 (Too Many Requests) と Retry-After ヘッダーについて解説しました。
- 429エラーとは: サーバーがパンクしないための「アクセス制限(レートリミット)」の合図。
- Retry-Afterとは: クライアントに向けた「あと〇秒待ってから来てね」という優しい配慮のメッセージ。
- エンジニアの心得: サーバーに負担をかけないよう、この待ち時間をしっかりと読み取って行儀よくリトライするプログラムを作ろう!
APIやインフラの仕組みは、一見すると無機質な暗号のようですが、紐解いてみると「お互いが気持ちよく通信するためのマナーや思いやり」で成り立っていることが分かりますよね。
次にエラーログで 429 を見かけたときは、「おっと、サーバーがちょっと休憩したがっているな。よし、Retry-After の時間を確認してスマートに待とう!」と、心の中でニヤリとしてみてください。
それでは、また次回の深淵なネットワークの世界でお会いしましょう!
コメント