Nginx のレート制限設定:高頻度の 404・400 スキャンリクエストに rate limit をかける

Nginx の limit_req と limit_conn の使い方を整理します。疑わしいスキャンパス、高頻度の 404/400 リクエスト、IP ごとの同時接続をどう制限するか、limit_req_zone の配置場所と主要パラメータの意味も説明します。

Web サイトのログに突然大量の 404400 が出る場合、原因は通常ユーザーがリンクを押し間違えたことではなく、自動スキャナーが .env.gitwp-adminphpmyadminxmlrpc.php といったパスを探っていることが多いです。

この種のリクエストには、いくつか問題があります。

  • access log が急速に大きくなる
  • error log が意味の薄い記録で埋まる
  • 静的サイトやリバースプロキシの接続が無効なリクエストに使われる
  • 本当に見るべき問題がスキャンノイズに埋もれる

Nginx では limit_reqlimit_conn を使って制限できます。ただし先に一点だけ重要です。Nginx は「レスポンスステータスが 404 または 400 だったら制限する」という処理を標準機能だけで直接行うことはできません。レート制限はレスポンス生成前に行われるためです。

実際には、404 / 400 を生みやすいスキャンパス、異常なアクセス元、サイト全体の高頻度アクセスを事前に制限します。

基本方針

まずは三層に分けるのがおすすめです。

  1. サイト全体にゆるいレート制限をかけ、単一 IP による高頻度アクセスを抑える。
  2. よくあるスキャンパスには厳しめの制限をかけ、直接 404 を返す。
  3. IP ごとの同時接続数を制限する。

より安全な導入順は、まずスキャンパスのルールと access_log off を入れて 1 日観察することです。それでもランダムパスの 404 が多い場合に、サイト全体の limit_req を追加します。

まず http でレート制限用 zone を定義する

limit_req_zonelimit_conn_zone は必ず http {} の中に置きます。個別サイトの server {} には置けません。

/etc/nginx/nginx.confhttp {} に直接書けます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
http {
    # 按客户端 IP 限速,普通页面请求
    limit_req_zone $binary_remote_addr zone=perip_general:20m rate=5r/s;

    # 更严格:疑似扫描路径
    limit_req_zone $binary_remote_addr zone=perip_scan:20m rate=1r/s;

    # 并发连接限制
    limit_conn_zone $binary_remote_addr zone=addr_conn:20m;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

別ファイルを作っても構いません。

1
sudo nano /etc/nginx/conf.d/limit-zones.conf

内容は次の通りです。

1
2
3
limit_req_zone $binary_remote_addr zone=perip_general:20m rate=5r/s;
limit_req_zone $binary_remote_addr zone=perip_scan:20m rate=1r/s;
limit_conn_zone $binary_remote_addr zone=addr_conn:20m;

前提として、nginx.confhttp {} 内で次が include されている必要があります。

1
include /etc/nginx/conf.d/*.conf;

次に server で zone を使う

サイト設定ファイルは通常 /etc/nginx/sites-enabled/www.example.com のような場所にあり、中身はたいてい server {} です。ここには limit_req_zone を書かず、前段で定義した zone だけを使います。

例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
server {
    root /srv/www/example.com;
    index index.html;
    server_name example.com www.example.com;
    access_log /var/log/nginx/example.com.access.log;
    error_log /var/log/nginx/example.com.error.log;

    # 全站温和限速:允许短时突发,降低误伤正常用户的概率
    limit_req zone=perip_general burst=30 nodelay;
    limit_conn addr_conn 20;

    # 对常见扫描路径严格限速,并关闭访问日志
    location ~* ^/(\.env|\.git|\.svn|wp-|wp/|adminer|phpmyadmin|pma|vendor|backup|config|server-status|cgi-bin|xmlrpc\.php) {
        access_log off;
        limit_req zone=perip_scan burst=5 nodelay;
        return 404;
    }

    # 你原来的 location /、listen ssl 等配置继续放这里
}

サイト全体のレート制限で通常アクセスを巻き込むのが心配なら、最初はスキャンパスだけにしてもよいです。

1
2
3
4
5
location ~* ^/(\.env|\.git|\.svn|wp-|wp/|adminer|phpmyadmin|pma|vendor|backup|config|server-status|cgi-bin|xmlrpc\.php) {
    access_log off;
    limit_req zone=perip_scan burst=5 nodelay;
    return 404;
}

各パラメータの意味

この行:

1
limit_req_zone $binary_remote_addr zone=perip_general:20m rate=5r/s;

意味は次の通りです。

  • limit_req_zone:リクエストのレート制限に使うカウント用 zone を定義する。
  • $binary_remote_addr:クライアント IP を制限キーにする。$remote_addr よりメモリ効率がよい。
  • zone=perip_general:20mperip_general という名前の共有メモリ領域を 20m のサイズで作る。
  • rate=5r/s:IP ごとに平均で毎秒 5 リクエストまで許可する。

この行:

1
limit_req_zone $binary_remote_addr zone=perip_scan:20m rate=1r/s;

基本は同じですが、より厳しい設定です。

  • perip_scan:疑わしいスキャンパス専用。
  • rate=1r/s:IP ごとに毎秒 1 リクエストだけ許可する。

この行:

1
limit_conn_zone $binary_remote_addr zone=addr_conn:20m;

意味は次の通りです。

  • limit_conn_zone:同時接続数制限に使うカウント用 zone を定義する。
  • $binary_remote_addr:ここでもクライアント IP ごとに集計する。
  • zone=addr_conn:20maddr_conn という名前の接続数カウント用共有メモリ領域を作る。

実際に同時接続数を制限するのは次です。

1
limit_conn addr_conn 20;

これは、各 IP の同時接続を最大 20 本にする、という意味です。

burst と nodelay の考え方

例えば:

1
limit_req zone=perip_general burst=30 nodelay;

次のように理解できます。

  • rate=5r/s:長期的な平均レートは毎秒 5 リクエスト。
  • burst=30:短時間の突発分として 30 リクエストまで許容する。
  • nodelay:平均レートを超えても burst 内なら待たせず処理し、burst を超えたら拒否する。

nodelay がない場合、Nginx は一部リクエストを遅延させてキューに入れようとします。通常の Web ページでは nodelay のほうが挙動を理解しやすいことが多いです。API や特に敏感なエンドポイントでは、実際の挙動に合わせて調整します。

よくあるエラー:limit_req_zone の配置場所が違う

次のようなエラーが出た場合:

1
2026/04/30 21:33:48 [emerg] 2290771#2290771: "limit_req_zone" directive is not allowed here in /etc/nginx/sites-enabled/example.com:9

これは、limit_req_zone を許可されていない場所に書いたという意味です。

よくある誤りは、server {} の中に置いてしまうことです。

1
2
3
4
5
6
7
8
9
server {
    root /srv/www/example.com;
    index index.html;
    server_name example.com www.example.com;

    limit_req_zone $binary_remote_addr zone=perip_general:20m rate=5r/s;
    limit_req_zone $binary_remote_addr zone=perip_scan:20m rate=1r/s;
    limit_conn_zone $binary_remote_addr zone=addr_conn:20m;
}

これは動きません。

覚え方はシンプルです。

  • limit_req_zone は「プールを定義する」ものなので http {} に置く。
  • limit_req は「プールを使う」ものなので server {} または location {} に置く。
  • limit_conn_zone は「接続プールを定義する」ものなので http {} に置く。
  • limit_conn は「接続プールを使う」ものなので server {} または location {} に置く。

明らかに異常な IP を一時的に拒否する

ログから、特定の IP が継続的にスキャンしていると確認できている場合は、一時的に拒否してもよいです。

1
2
3
4
5
deny 45.95.42.164;
deny 185.177.72.51;
deny 185.177.72.5;
deny 185.177.72.56;
deny 185.177.72.58;

この種の denyserver {} にも、特定の location {} にも置けます。長期的に残すかどうかは、誤検知リスクとアクセス元を見て判断します。

チェックしてリロードする

変更後はまず設定を確認します。

1
sudo nginx -t

問題がなければリロードします。

1
sudo systemctl reload nginx

いきなりサービスを再起動しないほうがよいです。reload なら Nginx が新しい設定を滑らかに読み込むため、リスクが低くなります。

推奨パラメータ

個人サイトや静的サイトなら、まずは次の値から始めるとよいです。

  • 通常ページ:rate=5r/s から 10r/s
  • スキャンパス:rate=1r/s
  • スキャンパスの burst=5
  • サイト全体の burst=30
  • IP ごとの同時接続:10 から 20

通常ユーザーのアクセスが少ないサイトなら、さらに厳しくできます。画像、スクリプト、API リクエストが多いサイトでは、通常ページの制限を少し緩めて、本物のアクセスを巻き込まないようにします。

一番安全なのは段階的に入れる方法です。

  1. まずスキャンパスに access_log off + return 404 を入れる。
  2. 次に perip_scan の厳しいレート制限を加える。
  3. 1 日ログを見る。
  4. ランダムパスの 404 がまだ多い場合、サイト全体のゆるいレート制限を有効にする。
记录并分享
Hugo で構築されています。
テーマ StackJimmy によって設計されています。