...

NGINXのレート制限:ボットトラフィックや攻撃に対する効果的な防御

NGINXのレート Limitingは、自動化されたリクエストを停止し、ピーク負荷を緩和するとともに、ログイン、API、フォームのエンドポイントをボットや攻撃から保護します。ここでは、制限を定義し、それを ボット対策 これを活用し、アクセス数の多いウェブサイト向けに堅牢なセキュリティ対策を構築する。.

中心点

重要な その主なポイントは以下の通りです:

  • 料金制限 悪意のある攻撃を阻止し、バックエンドリソースを保護します。.
  • バースト/ノーレイ ユーザーをブロックすることなく、正当なトラフィックのピークを吸収します。.
  • ゾーン 人間とボットを、それぞれ異なる制限で区別する。.
  • ロギング リミットを反復的に絞り込むためのデータを提供する。.
  • 統合 WAF、DDoS対策、およびモニタリングを組み合わせることで、その効果を高めます。.

レート制限が攻撃を早期に阻止する理由

攻撃者は高い リクエスト率, 、ログインフォームの悪用、APIへの過度な負荷、あるいはコンテンツの自動スクレイピングを防ぐためです。そのため、キーごと(通常はIPアドレスごと)にリクエスト数を制限し、リクエストを制限するか、遅延させるか、あるいは429を返すかを判断しています。 このようにして、ボットによるトラフィックをCPU、データベース、アプリケーションロジックから遮断し、正当なユーザーのみを通過させています。特に、/login、/auth、/xmlrpc.phpといった機密性の高いパスや、リソースを大量に消費する検索では、この手法の効果が顕著です。この手法の出典は、 Nginxのドキュメント ngx_http_limit_req_module について。.

実運用におけるNGINXモジュールの動作

このモジュールは、以下の原理に基づいて動作します。 漏れたバケツ- 仕組み:NGINX は各キーについて、ゾーン内にカウント値を保存し、それを許容レートと比較します。代表的なキーとしては、IP アドレス用の $binary_remote_addr、API キー用のトークン、あるいは map による派生値などがあります。 クライアントがレートおよびバーストバッファを継続的に超過する場合、NGINXはバックエンドにリクエストを転送する前にそのリクエストを拒否します。これにより、処理時間が節約され、実際の訪問者に対する遅延が軽減されます。この対応として、429 Too Many Requests または任意の他のステータスコードを設定します。 ステータスコード うーん

設定:手順ごとに詳しく解説

まず、httpセクション内のゾーンから始め、適度なレート設定を行い、影響を受けやすいパスに対してピンポイントで有効にします。 短時間のトラフィック急増には「バースト」を設定し、必要に応じて「nodelay」を指定して、急激な接続拒否を防ぐようにします。その後、ステージング環境でテストを行い、ログを分析してから本番環境に適用します。こうすることで、正当なユーザーに対して不必要なアクセス制限がかかるリスクを回避できます。具体的な例を挙げると、 構文 手に入る:

# http {}
limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=10r/s;

server {
  location /api/ {
    limit_req zone=req_limit_per_ip burst=20 nodelay;
    limit_req_status 429;
  }

  location /login {
    limit_req zone=req_limit_per_ip burst=5;
    limit_req_status 429;
  }
}

ゾーンとユーザーエージェントのロジックを用いたボット対策

IPベースの制限では、分散型ボットネットに対してはほとんど効果がありません。そのため、私はトラフィックを ゾーン: 人間にはより寛大な制限値を設定し、汎用クローラーにはより厳しい制限を課します。「map」を使ってユーザーエージェントを評価し、Googlebotなどの許可されたボットを識別して、それらには個別に厳重に監視された制限を設定します。 無名のスクレイパーに対しては、リソースを大量に消費するパスに厳しい制限を設けます。特定のパターンが検出された場合は、その レート 再び正常範囲に戻った。.

微調整:レート、バースト、ノードレイ、ステータスコード

「Rate」は1秒あたりのスループットを調整し、「Burst」は短時間のバッファリングを許可し、「nodelay」はバッファリングと即時通過のどちらを優先するかを決定します。 最初は控えめな設定、例えばAPIに対して10r/s、burst 20から始め、ログ分析の結果に基づいて調整します。ログインルートについては、ブルートフォース攻撃を抑制するために、例えば1r/sでburstを小さく設定します。 制限を超えた場合は429を返します。これにより、クライアントは 対処する そして、リトライロジックが適切に機能していること。特別なケースでは、クライアントからの要望に応じて代替のコードを使用することもあります。.

表による概要:ディレクティブとその用途

以下の通りである。 テーブル 主要な指針をまとめ、それらがいつ有用であるかを示しています。.

指令 効果 代表的な使用例
limit_req_zone キー、ゾーン、および レート 固く limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s; IP、トークン、またはユーザーエージェントごとの基準
limit_req [ ] 内の制限を有効にします ロケーション/サーバー limit_req zone=perip burst=20 nodelay; パスごと、またはvHostごとの詳細な制御
limit_req_status HTTPコードを設定します 超過 limit_req_status 429; 適切なクライアントの動作と再試行
地図 リクエストを転送する ゾーン に於いて map $http_user_agent $is_bot {…} ユーザーエージェントに基づくボットと人間の区別

実践:ログインエンドポイントを的確に保護する

私は /login を非常に厳しく制限しています。なぜなら、ボットが強力なパスワードを 頻度 試行錯誤する。1r/sにバースト3を設定すれば、実際のユーザーに過度な影響を与えることなく、大規模な推測攻撃を防ぐことができる。 さらに、繰り返し失敗したケースをログに記録し、そのIPを一時的にブロックするようにしています。2FAやオプションのキャプチャと組み合わせることで、データベースやセッション処理への負荷が著しく軽減されます。このようにして、失敗回数を最小限に抑え、 アクセス 安定して準備万端。.

実践:APIを公正かつ管理された形で提供すること

APIには明確な オッズ, 、個々のクライアントがスループット全体を占有しないようにするためです。 一般的なルートには10r/s、バーストは20に設定し、リソースを多く消費するエンドポイントにはより厳しい値を設定しています。トークンやAPIキーが利用可能な場合は、IPアドレスごとではなくトークンごとに制限を設けています。これにより、顧客間の公平性が確保され、悪用も防止されます。より詳しい解説については、私の記事「 APIのレート制限, 、この概念をより広い文脈で位置づけるものである。.

モニタリング、ロギング、および反復的な最適化

429のレスを含むログを記録しています キー (例:IPやトークン)とパスを分析し、パターンを特定します。ごく一部のパスでトラフィックの急増が見られる場合はスクレイピングやブルートフォース攻撃の兆候であり、トラフィックが分散している場合はボットネットの可能性が高いと言えます。 このデータを活用することで、必要な箇所にのみ制限を設け、誤検知を最小限に抑えています。レート、エラー率、レイテンシを表示するダッシュボードにより、変更ごとの効果を確認できます。これにより、 パフォーマンス 高い一方で、保護効果は高まっている。.

包括的な保護構想への組み込み

レート・リミッティングは、強力な第一の手段だと考えています , ですが、私はこれをWAFルール、IPレピュテーション、TLSの強化と組み合わせています。大規模な攻撃に対しては、NGINXが処理を行う前にネットワークレベルのトラフィックをフィルタリングする、上流側のDDoS対策が有効です。 私は継続的にメトリクスを計測し、異常なトラフィックの急増に対してアラートを設定し、ルールの更新を通じて対応しています。このようにして、複数の構成要素が組み合わさり、強靭な防御網が形成されます。これらについては、以下の資料で実践的な概要が紹介されています。 DDoS対策.

ボットと人間における具体的な設定パターン

map を使って訪問者をカテゴリ別に分類し、それぞれ別の ゾーン. 既知のクローラーには適度な制限を設け、汎用エージェントにはより厳しい制限を課します。/search や /report といったパスについては、CPU を大量に消費するため、より厳格な対応をとります。 違反が繰り返される場合、制限を緩和するのではなく、一時的にアクセスをブロックするか、検証をボット検知モジュールに移行します。これにより、 不正利用率 低く抑えつつ、検索エンジンの動作を妨げない。.

例:2つのゾーンとユーザーエージェントのマッピング

以下の断片は、以下の区分を示しています。 ユーザーエージェント そして、適切な制限値の設定です。私はこれを、きめ細かなステータスコードやロギングフィールドと組み合わせて、その効果を正確に測定しています。汎用エージェントを使用するボットは厳格なゾーンに振り分けられます。人間や認証済みのクローラーは、より緩やかなゾーンを利用します。このアプローチにより、計画可能な スループット 各クラスにつき:

map $http_user_agent $is_bot {
  default 0;
  "~*googlebot"     0;
  "~*bingbot" 0;
  "~*crawler|scraper|bot" 1;
}

limit_req_zone $binary_remote_addr zone=human:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=bot:10m   rate=1r/s;

server {
  location / {
    if ($is_bot) {
 limit_req zone=bot burst=5;
    }
    if ($is_bot = 0) {
 limit_req zone=human burst=20 nodelay;
    }
    limit_req_status 429;
  }
}

エラー処理:429を正しく伝える

Limitsでは、明確な 回答 再試行が適切なタイミングについての説明を添えて。APIの場合、クライアントがバックオフを適用できるよう、有効なRetry-Afterヘッダーを含める必要があります。人間のユーザーには、技術的な詳細を省いた簡潔な説明が表示されます。これにより、チケットの件数を減らし、動作の理解を促進します。すっきりとした UX 制限を許容できるものにし、フラストレーションを防ぐ。.

ホスティング、ネットワーク、カーネル:基盤を強化する

「Legit」トラフィックの増加と防御対策には、信頼性の高い リソース そして、ネットワークレベルでの適切なデフォルト設定。私は、最新のNGINXバージョンの使用、ゾーンに十分なRAMの確保、およびトランスポート攻撃に対する防御機能に注意を払っています。SYNフラッド対策としては、以下の有効化が有効です。 TCP SYNクッキー カーネル内で、接続が滞留しないようにします。結果として、NGINXの不要な負荷が軽減されます。このようにして、制限をHTTP層に集中させ、 スループット 安定している。

簡単にまとめると:NGINXのレート制限を効果的に活用する方法

私は問い合わせを以下ごとに制限しています キー, 、クリティカルパスを保護し、厳格なゾーン設定でボットを遠ざけます。バーストとノードレイを活用することで、不正利用を助長することなく、正当なトラフィックのピークを許容します。429ログに基づいて値を継続的に調整し、必要な箇所でのみ制限を強化しています。 WAF、DDoS防御、モニタリング、カーネルの強化と組み合わせることで、堅牢な保護コンセプトが構築されます。これを一貫して実施すれば、ボットトラフィックを大幅に削減し、 パフォーマンス 負荷がかかっている状態でも。.

実務においてしばしば欠けている要素

多くの設定では、レート制限の効果を顕著に高めるいくつかの重要な要素が欠けています:

  • プロキシの背後にある実際のクライアントIPアドレス: 適切なリアルIPの処理が行われない場合、NGINXはしばしばロードバランサーのIPに制限を設けてしまいます。その結果、その制限は、その背後に束ねられたすべてのユーザーに適用されてしまいます。.
  • ドライラン(Dry-Run): リミットは「盲目的に」発動されます。事前に、リミットが何回発動したかを記録しておくほうが良いでしょう。.
  • 微細な粒子の鍵: IPアドレスだけでなく、APIトークンごと、セッションごと、あるいはユーザーごとに制限を設けることで、公平性を高めることができます。.
  • limit_conn との連携: 並列接続とリクエストレートは、それぞれ異なる悪用パターンをカバーしています。.
  • 特定の例外: ヘルスチェック、Webhook、あるいは内部サービスについては、制限を緩く設定するか、あるいは制限を設けない場合が多い。.

リバースプロキシ:クライアントの実際のIPアドレスを安全に解析する

NGINXがロードバランサーの背後に配置されている場合、$binary_remote_addrが実際のクライアントを反映するように、Real-IPディレクティブを設定します。私は自分が所有するネットワークのみを信頼し、再帰的な評価を有効にします:

http {
  # 信頼できるプロキシIP範囲(例)
  set_real_ip_from 10.0.0.0/8;
  set_real_ip_from 192.168.0.0/16;
  # 必要に応じて、パブリックLB/CDNの範囲を追加

  real_ip_header X-Forwarded-For;
  real_ip_recursive on;

  limit_req_zone $binary_remote_addr zone=perip:20m rate=10r/s;
}

この設定を行わないと、制限が多くの無関係なユーザーに同時に適用されてしまう。設定完了後、アクセスログを確認して、想定通りのクライアントIPが記録されているかを確認する。.

主要戦略:IP、ユーザー、トークン、パス

選択する鍵によって、公平性と効果が決まります。実績のあるパターンをいくつか挙げます:

  • Pro IP ($binary_remote_addr): すぐに利用可能で、/login や匿名のエンドポイントに適しています。.
  • APIトークン1つにつき: 顧客間の公平性を確保し、NATバンドリングから保護します。mapを使用してトークンを抽出します。.
  • パスクラスごと: 高コストなエンドポイントを個別に制限する。例えば、/search を /status よりも厳しく制限する。.
map $http_authorization $api_token {
  default "";
  "~*^Bearer\s+(.+)$" $1;
}

limit_req_zone $api_token zone=per_token:30m rate=5r/s;

server {
  location /api/ {
    # トークンが存在する場合にのみ適用されます
    limit_req zone=per_token burst=10;
    limit_req_status 429;
  }
}

重要:キーのカーディナリティが高いと、ゾーン内のメモリを消費します。バッファを確保し、使用状況を監視してください。.

ゾーンの容量と設計

このゾーンは、アクティブなキーごとにメタデータを保存します。1エントリあたりの消費量は数十バイトに加え、オーバーヘッドがあります。そこから、私は次のように推測します:

  • 同時接続するIPアドレスやトークンの数が多い場合は、50~100 MBといった、より大きなゾーンを選択します。.
  • 最初は余裕を持って開始し、NGINXのログを確認します。「shared memory zone is full」というメッセージは、再チューニングが必要であることを示しています。.
  • 使用されていないキーは、短期間使用されないだけで失効します。ピーク値は1日の平均値よりも重要です。.

バーストとノードレイを的確に活用する

なし nodelay NGINXは、バーストバッファ内の超過を順に並べ、 遅延した リクエスト。使用方法: nodelay 許可されたバーストリクエストは即座に通され、それ以上のリクエストは拒否されます。私の対処法:

  • インタラクティブな経路 (HTML):nodelay を設定しない方が、厳しい 429 エラーではなく、短い待ち時間を生み出すことができる。.
  • API: 多くの場合、nodelay を指定して、クライアントが明確に 429 を受け取り、バックオフを実行するようにする。.
  • 高価なエンドポイント: バックエンドのピークを平滑化するための短いバースト。.

ドライラン、ログレベル、および評価

リミットを有効にする前に、ドライランを有効にしてログレベルを調整します。そうすれば、リスクを冒すことなくその効果を確認できます:

server {
  location /api/ {
    limit_req zone=perip burst=20;
    limit_req_dry_run on; # ブロックせず、ログのみ記録
    limit_req_log_level notice;  # 「error」よりも警告レベルが低い
  }
}

その後、3~7日分のアクセスデータを分析し、ホットスポットを特定して、レート/バーストを調整し、その後に初めてドライランを無効にします。.

429を適切に処理する:HTML、JSON、およびRetry-After

優れたUXを実現するために、ブラウザとAPIクライアントを区別し、 Retry-After. 私が制限を明確に伝える方法は次の通りです:

map $http_accept $wants_json {
  default 0;
  "~*application/json|/json"    1;
}

server {
  error_page 429 = @rate_limited;

  location @rate_limited {
    add_header Retry-After 2 always;
    if ($wants_json) {
 add_header Content-Type application/json;
      return 429 '{"error":"too_many_requests","retry_after":2}';
    }
    return 429 "後ほど再度お試しください。";
  }
}

APIはプログラムによって適切に対応できるため、ユーザーにはわかりやすいメッセージが表示されます。.

limit_req と limit_conn を組み合わせる

limit_req 各時間枠ごとの処理量を対象とし、, limit_conn 同時接続数を制限します。ダウンロード、チャットを多用するクライアント、あるいはHTTP/2のトラフィック急増に対しては、以下の2つの方法を組み合わせています:

limit_conn_zone $binary_remote_addr zone=perip_conn:10m;

server {
  location /api/ {
    limit_req  zone=perip burst=20 nodelay;
    limit_conn zone=perip_conn 20;  # IPアドレスあたり最大20の同時接続
  }
}

こうすることで、少数のクライアントがレートは遵守しているものの、同時接続数が多すぎてリソースを占有してしまうのを防ぐことができます。.

例外、ヘルスチェック、および内部ルート

すべてのパスに制限が必要というわけではありません。ヘルスチェック(/healthz)、内部Webhook、または決済コールバックには、limit_reqを設定しない、あるいはより緩やかな値を設定した独自のロケーションが割り当てられます:

server {
  # ヘルスチェックに制限なし
  location = /healthz { return 200 "ok"; }

  # 決済コールバックに対する緩やかな制限
  location /webhooks/pay/ {
    limit_req zone=perip burst=5;
  }

  # ログインに対する厳格な保護
  location = /login {
    limit_req zone=perip rate=1r/s burst=3;
  }
}

きめ細かな例外処理により、誤検知を減らし、統合の安定性を維持します。.

「If」の魔法を使わない、より堅牢なゾーンルーティング

「ボット対人間」の区別については、named locations による内部リダイレクトを好んで使っています。これにより、設定が明確で予測可能になります:

map $http_user_agent $is_bot {
  default 0;
  "~*googlebot|bingbot" 0;
  "~*crawler|scraper|bot" 1;
}

limit_req_zone $binary_remote_addr zone=human:20m rate=10r/s;
limit_req_zone $binary_remote_addr zone=bot:10m   rate=1r/s;

server {
  error_page 418 = @bot;

  location / {
    if ($is_bot) { return 418; }   # 内部ルーティング
    limit_req zone=human burst=20 nodelay;
    limit_req_status 429;
    try_files $uri $uri/ /index.html;
  }

  location @bot {
    limit_req zone=bot burst=5;
    limit_req_status 429;
  }
}

このように、ボットは決定論的に「厳格なゾーン」に、人間は「リラックスしたゾーン」に分類される――両方の制限が同時に作用することはない。.

テスト、測定、試着:実用的な手順

  • ステージング: レート/バーストを控えめに設定し、ドライランを有効にし、合成負荷をホットパスに対して実行する。.
  • スモークテスト: curl や Lasttools を使って短いバーストを生成し、429/Delay の挙動を確認する。.
  • 本番環境パイロット: まずは個々のロケーションに適用し、ログを綿密に監視する。.
  • 反復研ぎ: パターンが確認された場所でのみ制限を強化し、誤検知を最小限に抑える。.
# の例:curl を使った高速バーストテスト
for i in {1..50}; do curl -s -o /dev/null -w "%{http_code}\n" https://example.com/login & done; wait

秒単位ではなく分単位のレートと、きめ細かなパス

NGINX では、秒単位または分単位でのレート設定が可能です(r/s, r/m). ログインの不正利用を防ぐため、正当な短時間のダブルクリックは許可しつつ、連続クリックを制限するために、1r/s ではなく 60r/m を設定することがよくあります。高価なパスには、安価なものよりも厳しい制限を設けます。例:

limit_req_zone $binary_remote_addr zone=perip_min:20m rate=60r/m;

server {
  location /search/ {
    limit_req zone=perip_min burst=10;   # 厳格
  }
  location /status {
    # 制限なし – 低コストで内部利用
    return 200;
  }
}

つまずきの原因と、それを回避する方法

  • 間違った鍵: リアルIPを持たないプロキシを経由している場合、うっかりすべてのユーザーに対して一様に制限をかけてしまうことがあります。.
  • ゾーンが小さすぎる:「zone is full」と表示されると予期せぬ動作を引き起こすため、余裕を持ってサイズを設定すること。.
  • 何事にも限度がある: パスが異なれば、必要な値も異なります。画一的な対応は不満を招きます。.
  • モニタリングなし: 429の分析を行わなければ、設定ミスは見過ごされてしまう。.
  • ホワイトリスト外: 例外の範囲が広すぎると、あらゆる悪用を許すことになる――的を絞り、一時的かつ追跡可能な形でホワイトリスト化すべきだ。.

HTTP/2、SSE、およびキャッシュに関する特徴

HTTP/2は、少数の接続でリクエストを束ねます;; limit_conn それでもなお重要な問題です。なぜなら、ストリームにはリソースがかかるからです。サーバー送信イベントや長時間のダウンロードは、レート制限が適用されることはめったにありませんが(リクエスト数が少ないため)、時間を消費します。このような場合、私は `limit_conn` を使って並行処理を制限するか、帯域幅戦略を適用しています。可能な場合は、 キャッシング (例:静的アセット、頻繁なGETリクエストなど)これにより、制限が適用される頻度が減り、ユーザーはより迅速な応答を得られるようになります。.

手術チェックリスト

  • Real‑IP は正しい、キーが定義されている(IP/トークン/ユーザー)
  • ゾーンは十分な規模で設計されており、メトリクス/ログも整備されている
  • パスクラスごとにレート/バーストを調整し、nodelayを意図的に設定
  • ドライランテストを実施し、429通信(Retry-After)を実装した
  • Health/Webhooks の例外、limit_conn との併用
  • 反復的な再調整と異常時のアラート通知

現在の記事

データセンター内のLinuxサーバーにおける、可視化されたプレッシャーストール情報の指標
管理

Linux PSI:正確なパフォーマンス分析と監視

LinuxのPSI(Pressure Stall Information)は、CPU、メモリ、I/Oがシステムのパフォーマンスをどの程度低下させているかを可視化します。PSIを有効にし、正確なパフォーマンス監視に活用する方法をご紹介します。.