ひとことで言うと#
一定時間内に受け付けるリクエスト数に上限を設け、超過分を拒否または遅延させることで、サービスを過負荷や悪用から守る仕組み。
押さえておきたい用語#
- トークンバケット
- 一定速度でトークンが補充され、リクエスト時にトークンを消費するアルゴリズム。バースト許容と安定制限のバランスが良い。
- スライディングウィンドウ
- 直近N秒間のリクエスト数で制限する方式。固定ウィンドウより滑らかな制御が可能。
- HTTP 429
- Too Many Requestsを示すHTTPステータスコード。制限超過時に返却する標準レスポンス。
- Retry-After
- 制限超過時にクライアントに再試行可能なタイミングを伝えるHTTPレスポンスヘッダー。
- バースト
- 短時間に集中して送信されるリクエストの急増。正当な利用でもバーストは発生する。
レートリミティングの全体像#
こんな悩みに効く#
- 特定のユーザーやクライアントが大量リクエストを送り、他のユーザーに影響が出ている
- APIが想定以上のトラフィックで応答不能になる
- ボットやスクレイパーにリソースを食い潰される
基本の使い方#
用途に合ったレート制限アルゴリズムを選ぶ。
- 固定ウィンドウ: 時間枠ごとにカウントをリセット(例: 1分間に100回まで)
- スライディングウィンドウ: 直近N秒間のリクエスト数で制限(より滑らかな制御)
- トークンバケット: 一定速度でトークンが補充され、リクエスト時にトークンを消費。バースト対応可能
- リーキーバケット: リクエストをキューに入れ、一定速度で処理。安定した流量を実現
ポイント: トークンバケットが最も汎用的。バースト許容と安定制限のバランスが良い。
何を基準に、どの程度制限するかを決める。
- 制限の単位: IPアドレス、APIキー、ユーザーID、エンドポイントなど
- 制限値: 「1分間に60リクエスト」「1日10,000リクエスト」など
- 階層的ルール: プランごとに異なる制限値(Free: 100/分、Pro: 1000/分)
ポイント: 制限の粒度が粗すぎると正当なユーザーを巻き込み、細かすぎると管理が煩雑になる。
クライアントが制限状況を把握できる情報を返す。
X-RateLimit-Limit: 制限値X-RateLimit-Remaining: 残りリクエスト数X-RateLimit-Reset: 制限リセットまでの時間- 制限超過時はHTTP 429 Too Many Requestsを返し、Retry-Afterヘッダーを付与する
ポイント: クライアントに制限状況を透明に伝えることで、適切なリトライ実装を促す。
複数サーバーでレートリミットの状態を共有する。
- Redisなどのインメモリストアでカウンターを一元管理する
- APIゲートウェイ層で統一的に制限を適用する
- レースコンディションを防ぐためにアトミック操作を使用する
ポイント: ローカルカウンターだけでは、ロードバランサー配下で制限が緩くなる。
具体例#
状況: SaaSプロダクトの公開API。特定のクライアントが1秒間に数百リクエストを送り、他のユーザーのP95レスポンスタイムが200ms→800msに悪化。
設計: トークンバケットアルゴリズム。Redisでカウンター管理。
- Freeプラン: 60リクエスト/分(バースト許容: 10/秒)
- Proプラン: 600リクエスト/分(バースト許容: 30/秒)
- 制限超過時: HTTP 429 +
Retry-After: 5を返却
結果: 過剰リクエストが制限され、全ユーザーのP95レスポンスタイムが200ms→80msに改善。APIキーごとの使用状況が可視化され、プランアップグレードの営業機会も創出。
状況: ログインAPIに対して1つのIPアドレスから1秒間に50回のリクエスト。パスワードのブルートフォース攻撃の疑い。
対策: IPアドレス単位 + アカウント単位の二重レートリミット。
- IPアドレス: 10リクエスト/分(ログインAPIのみ)
- アカウント: 5回失敗で15分ロック
- 制限超過時: 通常の429ではなく意図的に遅延レスポンス(タールピット)を返す
結果: ブルートフォース攻撃の成功率がゼロに。正当なユーザーへの影響なし(通常のログインは1分に10回も試行しない)。
状況: 注文サービスが在庫サービスを呼び出し。在庫サービスの障害時に、注文サービスがリトライを繰り返し、在庫サービスに毎秒5,000リクエストが殺到。復旧が遅延する悪循環。
対策: サービス間通信にもレートリミットを適用。
- 注文→在庫: 最大1,000 RPS(通常ピークの2倍)
- 超過時はサーキットブレーカーと連携して即座にフォールバック
- 在庫確認は「在庫あり」をキャッシュから返し、後で整合性を確認
結果: 在庫サービスの障害時、注文サービスの応答時間は50ms以内を維持(キャッシュフォールバック)。在庫サービスの復旧時間が10分から3分に短縮。
やりがちな失敗パターン#
- 制限超過時に情報を返さない — 429だけ返してRetry-Afterやリセット時刻を伝えないと、クライアントが即座にリトライして更に負荷をかける。レスポンスヘッダーで制限情報を透明に伝える
- 全エンドポイントに同じ制限をかける — 軽量なGETと重いPOSTに同じ制限は不適切。エンドポイントの負荷特性に応じて制限値を分ける
- 内部サービス間通信にもそのまま適用する — 内部通信に外部向けのレートリミットをかけると、正常なバッチ処理が止まる。内部通信は別ルールを設定する
- 制限値を本番データなしで決める — 勘で制限値を設定すると正当なユーザーを制限してしまう。本番のアクセスパターンを分析してから制限値を決める
まとめ#
レートリミティングはAPIを守る基本的な防御機構。適切なアルゴリズム選択、クライアントへの透明な情報提供、分散環境での一貫性確保が成功のポイント。導入することでサービスの安定性を確保しつつ、利用状況の可視化という副次的なメリットも得られる。