Nginx 防爬虫配置为什么总是失效?
Nginx防爬虫限速失效多因限速规则未放在http块、CDN或反向代理下未用realip获取真实IP,以及只拦UA不拦访问行为;应正确配置limit_req_zone和limit_conn并启用real_ip模块,否则规则无效或误伤全站。
Nginx 防爬虫配置失效,九成不是配置写错,而是三个前提没满足:限速用的 key 拿不到真实访客 IP、改完配置没真正生效、只拦 User-Agent(UA,浏览器或爬虫自报的身份字符串)不拦访问行为。把这三件事逐条对齐,同一份配置才会从“写了等于没写”变成“当场见效”。
为什么 Nginx 限速规则对爬虫完全没效果?
结论:如果你把 limit_req_zone 写在 server 块里,或者 key 用了 $remote_addr 但站点前面挂着 CDN,限速规则基本等于关闭。
limit_req_zone 只能定义在 http 块中,写在 server 或 location 里,nginx -t 会直接报 "limit_req_zone" directive is not allowed here,配置文件根本没被加载。正确写法是:
http {
limit_req_zone $binary_remote_addr zone=req_per_ip:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;
limit_req_status 429;
}
server {
location / {
limit_req zone=req_per_ip burst=20 nodelay;
limit_conn conn_per_ip 10;
}
}
这里两个参数容易踩坑:key 用 $binary_remote_addr 而不是 $remote_addr,前者是 4 字节二进制,后者是 15 字节字符串,10m 共享内存能记录的 IP 数量差 3 倍以上;burst=20 nodelay 表示允许瞬时 20 个请求排队且不延迟放行,不写 nodelay 会变成匀速排队,正常用户的页面加载会明显变卡。另外 Nginx 默认超限返回 503,1.3.15 之后才有 limit_req_status,设成 429 更利于日志筛查。
真实 IP 怎么拿?CDN 和反向代理下的 real_ip 配置
结论:站点前面有 CDN、WAF 或 SLB 时,$remote_addr 一律是代理节点 IP,此时按 IP 限速等于对 CDN 限速,全站用户一起被 429。
解决方式是启用 ngx_http_realip_module(Nginx 编译时默认不带,需要 --with-http_realip_module),把上游代理段设为可信来源:
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 你的CDN回源网段;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
real_ip_recursive on 的作用是从右往左跳过可信代理,取第一个非可信 IP 作为真实客户端,不做这一步,攻击者在请求头里自己伪造一个 X-Forwarded-For 就能把封禁引到别人身上。改完后用日志验证:log_format 里加上 $remote_addr $http_x_forwarded_for,tail -f 看几行,如果 $remote_addr 还是 100.64.* 或 CDN 段,说明 set_real_ip_from 没覆盖到回源段。
只封 User-Agent 为什么没用?
结论:UA 是客户端自己填的字符串,curl -A "Mozilla/5.0" 一行命令就能伪装,UA 黑名单只能挡掉最懒的那批采集器。
UA 拦截的正确用法是低成本过滤,不是主力防护。用 map 比写一堆 if 更安全(if 在 location 内属于 rewrite 阶段,容易和其他指令冲突):
map $http_user_agent $bad_ua {
default 0;
"" 1; # 空 UA 直接拦
~*(python-requests|scrapy|curl|wget|Go-http-client) 1;
}
server {
if ($bad_ua) { return 403; }
}
真正需要花力气的是行为维度:按 IP 的请求频率、单位时间内的 404 比例、是否只抓列表页不加载 CSS/JS。分布式爬虫用几百个 IP 轮换,单 IP 限速挡不住,这种情况得上 JS 挑战页或直接按 UA+IP 段做 ipset 封禁。
怎么避免把百度蜘蛛一起封掉?
结论:防爬虫误伤搜索引擎蜘蛛的代价,是网站收录掉、关键词排名下滑,恢复周期通常 2 到 4 周。
上线规则前先做两件事:一是把百度、Google、搜狗的官方蜘蛛 IP 段加入白名单放行(geo 模块或 allow 指令均可);二是观察 3 天日志里被 429/403 的 UA 分布,确认没有 Baiduspider。这一步在站长社区里被反复提醒,因为一旦把蜘蛛限速,抓取频次下降、新页面不进索引,后面再靠 SEO 优化 补回来要花几倍时间。用宝塔面板 的站点日志和 Nginx 状态页配合看,能快速定位是哪条规则在拦。
改完配置怎么确认真的生效?
结论:nginx -t 只验语法,不验逻辑,必须用真实请求压测才算验证通过。
三步走:nginx -t && nginx -s reload 加载配置;用 ab -n 200 -c 20 或 for i in $(seq 1 50); do curl -s -o /dev/null -w "%{http_code}\n" 你的URL; done 打同一个 IP,看是否按预期出现 429;再 tail -f access.log 确认状态码和真实 IP 对得上。如果压测全返回 200,说明 location 没命中、规则没进 http 块,或者命中了更优先的正则 location。
收个尾:防爬虫失效基本逃不出三件事——key 拿的是代理 IP、zone 写错了位置没加载、只靠 UA 不靠频率。把 real_ip 配好、limit_req_zone 放进 http 块、日志验证跑通,配置才算真正落到网站上,网站流量 的异常曲线也才看得懂。
你现在的站点前面有没有挂 CDN?被限速的请求里,$remote_addr 显示的是真实 IP 还是 10 段内网地址?
转载请注明出处,版权归原作者所有。