第31回: レート制限の高度化設計 — 全ユーザーに同じ制限をかけていませんか?

章: 第8章: パフォーマンスと本番運用実践

「一律60リクエスト/分」で本当に十分ですか?

未認証のスクレイパーも、プレミアムプランのユーザーも、同じ制限値が適用されている——こうした設計は、正規ユーザーの体験を損ないながら悪意ある攻撃には十分機能しないことがあります。

レート制限は「一律」から「属性別」に進化させることで、正規ユーザーへの影響を最小化しながら、不正アクセスを効果的に遮断できます。

問題の本質と解決策

問題: 全ユーザーに同じ制限を適用すると、APIをヘビーに使う正規ユーザーがブロックされる一方、認証情報を持つ攻撃者には制限が緩すぎる場合があります。

解決策: RateLimiter::for() でユーザーの認証状態やプラン属性に応じて制限値を分岐します。未認証はIPアドレスで厳しく制限し、認証済みはユーザーIDで緩やかに制限するのが基本パターンです。

一律制限 vs 属性別制限 比較

観点 一律制限 属性別制限(認証状態・プラン別)
正規ユーザーへの影響 ヘビーユーザーがブロックされやすい プランに応じた適切な制限
攻撃者への制限 認証済み攻撃者に緩い 未認証は厳しくIPで制限
実装の柔軟性 固定値のみ 動的に計算可能
運用のしやすさ シンプル 分岐が明確で変更しやすい

チェックポイント: 未認証ユーザーと認証済みユーザーで制限値を分けていますか?同一値を設定しているなら、まずここから見直しましょう。

実装サンプル


<?php
RateLimiter::for('api', function (Request $request) {
    if ($request->user()) {
        return Limit::perMinute(120)->by('user:' . $request->user()->id);
    }
    return Limit::perMinute(30)->by('ip:' . $request->ip());
});

まとめ & 次のステップ

  • レート制限はユーザー属性(認証状態・プラン)ごとに分けることで実用性が上がります
  • 未認証はIPベース・認証済みはユーザーIDベースで制限するのが基本パターンです
  • 制限超過時のレスポンス(429)には Retry-After ヘッダを付けてUXを改善します
  • まずは未認証と認証済みを分けるだけでも大きな効果があります
  • 導入後は制限ヒット率を監視して閾値を調整していきましょう

次回は ゼロダウンタイムデプロイ基礎 を学びます。サービスを止めずにデプロイするための順序設計を解説します。

Related Articles