Nginx 限速設定:對高頻 404、400 掃描請求加 rate limit

整理 Nginx limit_req 和 limit_conn 的用法:如何對疑似掃描路徑、高頻 404/400 請求和單 IP 併發存取做限速,並說明 limit_req_zone 放置位置和常見參數含義。

網站日誌裡如果突然出現大量 404400,常見原因不是正常使用者點錯連結,而是自動掃描器在探測 .env.gitwp-adminphpmyadminxmlrpc.php 這類路徑。

這類請求會帶來幾個問題:

  • access log 被快速刷大
  • 錯誤日誌裡充滿無意義記錄
  • 靜態網站或反代服務被大量無效請求占用連線
  • 真正的問題被掃描噪音淹沒

Nginx 可以用 limit_reqlimit_conn 做限制。不過要先說明一點:Nginx 原生不能直接按「回應狀態碼是 404 或 400」再限速,因為限速發生在回應產生之前。

實際做法是:對容易產生 404 / 400 的掃描路徑、異常來源和全站高頻請求提前限速。

基本思路

推薦先分成三層:

  1. 全站溫和限速,避免單個 IP 高频刷站。
  2. 對常見掃描路徑嚴格限速,並直接返回 404
  3. 對單 IP 併發連線數做限制。

更穩妥的上線順序是:先加掃描路徑規則和 access_log off,觀察一天;如果還有大量隨機路徑 404,再加全站 limit_req

先在 http 裡定義限速池

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.conf 裡確實在 http {} 中包含了:

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

再在 server 裡使用限速池

站點設定檔一般在 /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:定義請求限速用的計數池。
  • $binary_remote_addr:按客戶端 IP 做限速 key,比 $remote_addr 更省記憶體。
  • zone=perip_general:20m:建立名為 perip_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:定義併發連線限制用的計數池。
  • $binary_remote_addr:仍然按客戶端 IP 統計。
  • zone=addr_conn:20m:建立名為 addr_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 會嘗試把部分請求排隊延遲處理。對普通網頁來說,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;

這類 deny 可以放在 server {} 裡,也可以放在具體 location {} 裡。是否長期保留,要看誤傷風險和存取來源。

檢查並重載

改完先檢查設定:

1
sudo nginx -t

沒有問題再重載:

1
sudo systemctl reload nginx

不要直接重啟服務。reload 會讓 Nginx 平滑載入新設定,風險更小。

推薦參數

如果只是普通個人網站或靜態網站,可以先用下面這組:

  • 普通頁面:rate=5r/s10r/s
  • 掃描路徑:rate=1r/s
  • 掃描路徑 burst=5
  • 全站 burst=30
  • 單 IP 併發:1020

如果正常使用者流量很小,參數可以更嚴格;如果網站有大量圖片、腳本、介面請求,普通頁面限速要放寬一些,避免誤傷真實存取。

最穩的處理方式是分階段上線:

  1. 先對掃描路徑 access_log off + return 404
  2. 再加 perip_scan 嚴格限速。
  3. 觀察一天日誌。
  4. 如果隨機路徑 404 仍然很多,再開啟全站溫和限速。
记录并分享
使用 Hugo 建立
主題 StackJimmy 設計