如何用 Nginx 的 rewrite 规则屏蔽特定 URL 的爬虫?
用 map 按 User-Agent 标记爬虫、在限定 URL 的 location 内用 if 返回 403/444,rewrite 无法拒绝请求;注意 map 只能置于 http 上下文,if 内避免 proxy_pass 等复杂指令。
想屏蔽指定 URL 上的爬虫,正确的组合是「map 定义爬虫 UA 变量 → location 限定 URL 范围 → if 里 return 403 或 444」;rewrite 只负责把爬虫请求改写到拦截页,它本身不具备拒绝请求的能力。把这三者混用清楚,配置就能既拦住爬虫,又不误伤正常访客和 Googlebot、Baiduspider 这类合规引擎。
为什么单靠 rewrite 屏蔽不了爬虫?
rewrite 的唯一职责是按正则改写请求 URI,语法是 rewrite regex replacement [flag],flag 取 last / break / redirect / permanent。它决定请求被送去哪个 location,但不决定返回什么状态码。
把爬虫 rewrite 到 /deny.html 之后,Nginx 仍然返回 200,采集器会把提示页当正常内容收进索引,你一次数据库查询都没省下。真正拒绝请求要用 return:return 403 返回标准拒绝响应,return 444 是 Nginx 私有状态码,直接断开 TCP 连接、不回任何响应头,带宽消耗最低。
怎么按 User-Agent 批量识别爬虫?
用 map 在 http 块建立 UA 到标志位的映射,比在十几个 location 里重复写正则更好维护。~* 表示大小写不敏感正则匹配。
http {
map $http_user_agent $bad_bot {
default 0;
~*AhrefsBot 1;
~*SemrushBot 1;
~*MJ12bot 1;
~*DotBot 1;
~*Bytespider 1;
~*PetalBot 1;
}
}
关键约束:map 只能写在 http 上下文,放进 server 或 location 会让 nginx -t 直接报错。多个关键词也可以写成 ~*(AhrefsBot|SemrushBot|MJ12bot) 一行,但每个 bot 单列一行便于后期增删。
怎么只屏蔽特定 URL 上的爬虫?
结论:把 if 放进限定路径的 location 内,爬虫只在你点名的 URL 上被拦,其他页面照常访问。
server {
listen 443 ssl;
server_name www.example.com;
location ~* ^/(search|api/list|ajax/price) {
if ($bad_bot) {
return 403;
}
# 该路径的正常业务配置
}
}
三个要点:
location ~*是大小写不敏感的正则匹配,优先级高于普通前缀匹配;若希望某个目录整段跳过正则,改写为location ^~ /static/。if块内只放return、rewrite、set、add_header这几类简单指令是安全的;一旦把proxy_pass、fastcgi_pass写进 if,就落入官方文档标注的if is evil未定义行为,表现为部分请求跳过反代或直接 502。需要反代时,把if ($bad_bot) { return 403; }放在 location 顶层,proxy_pass放在它后面。return 444在 curl 里显示为Empty reply from server,这是预期现象,不是配置失败。
一定要用 rewrite 改写怎么办?
场景是「想让爬虫看到一条明确的 410 声明,而不是冷冰冰的 403」时,rewrite 才有意义:
location ~* ^/search {
if ($bad_bot) {
rewrite ^ /bot-deny.html last;
}
}
location = /bot-deny.html {
internal;
return 410;
}
internal 保证外部直接访问该地址返回 404,只有内部跳转可达。410 表示资源永久移除,主流搜索引擎收到后会加速删除旧索引,效果强于返回 200 的提示页。
怎么验证配置生效?
改完先做语法检查再平滑重载,然后用 curl 指定 UA 做双向验证:
nginx -t && nginx -s reload
curl -A "AhrefsBot/7.0" -I https://www.example.com/search?q=1
curl -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" -I https://www.example.com/search?q=1
第一条期望 HTTP/2 403(用 444 则连接被断开),第二条期望 HTTP/2 200。上线前先统计真实日志,用数据确认该封谁:
awk -F'"' '{print $6}' access.log | sort | uniq -c | sort -rn | head -20
有哪些必须知道的边界?
- UA 可任意伪造,此方案挡的是使用默认 UA 的批量采集器,不构成安全防护。对抗恶意采集要叠加
limit_req zone=oneburst=5 nodelay;频率限制,或接入 fail2ban、WAF。 - 不要顺手把
Googlebot、Bingbot、Baiduspider写进 map,会导致收录量在 7 到 30 天内明显下滑。 444不返回任何说明,对可能申诉的爬虫慎用;对外声明用 403 或 410 更稳妥。- 规则改动集中在 http 块,多站点共用同一份 map,改一处全站生效。
总结一下:map 负责识别爬虫、location 负责圈定 URL、if + return 负责拒绝、rewrite 只用于跳转到拦截页,四者分工明确就不会互相打架。上线顺序是先看日志确认 UA,再小范围限定路径,最后用 curl 双 UA 验证,全程 nginx -t 兜底。
转载请注明出处,版权归原作者所有。