Nginx 用 limit_conn 限制单 IP 并发连接防爬虫怎么配?
通过确认 http_limit_conn_module、在 http 块以 $binary_remote_addr 定义 limit_conn_zone,并在 server 或 location 设置 limit_conn 阈值、429 状
学完这篇你能得到一套可直接抄进配置文件的 Nginx 单 IP 并发连接限制方案,配完后同一个 IP 同时挂起的连接超过阈值就会被拒绝,爬虫那种「一口气开几百个连接」的打法会当场失效。
第一步:确认模块已经编进 Nginx
这一步是为了确认你的 Nginx 支持 limit_conn,做完能确定后面写配置不会报 unknown directive。
执行:
nginx -V 2>&1 | tr ' ' '\n' | grep -i limit_conn
正常会输出 --with-http_limit_conn_module。Debian/Ubuntu 的 nginx 包、CentOS 的 nginx 包、官方源安装的版本默认都带这个模块,一般不用重新编译。
注意:
nginx -V的大写 V 才是看编译参数,小写-v只输出版本号,别搞混。
第二步:在 http 块定义共享内存区
这一步是告诉 Nginx「按什么维度统计连接数、给多少内存」,做完才有后面能引用的计数器名字。
打开主配置 /etc/nginx/nginx.conf,在 http { ... } 大括号内部加一行:
http {
limit_conn_zone $binary_remote_addr zone=perip:10m;
...
}
$binary_remote_addr:用二进制形式的客户端 IP 做 key,IPv4 只占 4 字节,比$remote_addr省内存。zone=perip:10m:计数器名叫perip,分配 10MB。64 位系统上每个状态约 32 字节,10MB 大概能存 3 万多个 IP 的计数。
注意:
limit_conn_zone只能写在http块里。如果你习惯把配置拆到/etc/nginx/conf.d/*.conf,那些文件里的内容本身就是 http 上下文,写在那里也行;但写进server {}里会直接报"limit_conn_zone" directive is not allowed here,启动失败。
第三步:在 server 或 location 里启用限制
这一步是真正打开开关,做完后单 IP 并发超过阈值就会被拒。
在站点的 server { } 里加:
server {
listen 80;
server_name example.com;
limit_conn perip 10;
limit_conn_status 429;
limit_conn_log_level warn;
location / {
proxy_pass http://127.0.0.1:8080;
}
}
limit_conn perip 10:同一个 IP 最多同时 10 个连接,第 11 个直接拒。名字perip必须和第二步的 zone 名完全一致。limit_conn_status 429:被限时返回 429(默认是 503)。429 语义更准,也方便你在日志里和真实故障区分。limit_conn_log_level warn:把限流日志级别调到 warn,默认是 error,日志里会带上limiting connections by zone "perip"。
改完先测语法再重载,别直接 reload:
nginx -t && nginx -s reload
两条命令用 && 连起来,语法不过就不会执行重载,线上配置不会被改坏。
注意:
limit_conn统计的是「已经读完请求头、正在处理中」的连接,不是 TCP 连接数。慢速攻击那种只发一半 header 的连接归limit_req或client_header_timeout管,别指望limit_conn全包。
第四步:套了 CDN 或反向代理时先取真实 IP
这一步是防止「限制对象搞错」——如果流量经过 CDN,$binary_remote_addr 拿到的是 CDN 回源节点的 IP,你限的会是 CDN,不是爬虫。
在 http 块加 realip 配置:
http {
set_real_ip_from 173.245.48.0/20; # 你的 CDN 回源网段
set_real_ip_from 103.21.244.0/22;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
}
配完 $binary_remote_addr 才会变成真实访客 IP。
注意:
set_real_ip_from一定要写你上游的固定回源网段,写成0.0.0.0/0等于允许任何人伪造X-Forwarded-For绕过限流,这是很常见的配置事故。
第五步:验证限制是否真的生效
这一步确认配置有效,做完你能看到明确的拒绝行为。
用 ab 打 20 个并发:
ab -n 100 -c 20 http://example.com/
如果阈值设的是 10,输出里会出现 Non-2xx responses,数量大约是一半左右。同时 tail -f /var/log/nginx/error.log 能看到:
limiting connections by zone "perip", client: 1.2.3.4, ...
也可以直接看状态码:
curl -o /dev/null -s -w '%{http_code}\n' http://example.com/
第六步:按业务调参
阈值没有万能值,给几个参考起点:
- 普通资讯站、企业站:
limit_conn perip 10; - 有大量图片/接口并发的前端页面:
20到30,因为浏览器对单域名并发本来就是 6 左右,加上静态资源分域名会叠加 - 下载站或 API:单独给
location /download/设更低的值,比如limit_conn perip 2;,location里的配置会覆盖server级
注意:
limit_conn只管并发数,不管请求频率。爬虫串行慢慢爬的时候并发永远是 1,这条路走不通。真实场景建议和limit_req zone=xxx rate=5r/s burst=10 nodelay;一起上,两个模块互补。
小结
limit_conn_zone必须写在http块,limit_conn写在server或location,两处的 zone 名要一模一样。- key 用
$binary_remote_addr,比$remote_addr省内存,10MB 约存 3 万 IP。 - 加
limit_conn_status 429和limit_conn_log_level warn,日志和状态码都更好排查。 - 前面有 CDN 必须先配
realip且只信任回源网段,否则限错对象或被人伪造头绕过。 - 改完一律
nginx -t && nginx -s reload,别裸 reload。 - 它只防并发不防频率,配合
limit_req才有完整防护。
转载请注明出处,版权归原作者所有。