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 设计