Nginx 返回 444 拦截爬虫的配置教程。
用 map 匹配 $http_user_agent、命中后执行 return 444 直接断开连接,是成本最低的爬虫拦截方式;相比 403/404 不返回任何响应内容,也不给爬虫反馈。配置时 map 放在 http 块,return 444
在 Nginx 里拦截爬虫最省资源的方式是:用 map 指令匹配 $http_user_agent,命中后执行 return 444;。444 是 Nginx 自己定义的非标准状态码,它不返回任何响应头、不返回响应体,直接关闭 TCP 连接;客户端(比如 curl)看到的是 Empty reply from server。与返回 403 页面相比,444 省掉了全部响应数据的传输和日志中的 body 记录成本,是成本最低的拒绝方式。
444 和 403、404 有什么区别,为什么拦爬虫要用 444
结论:444 表示「已收到请求但直接断开连接」,403 表示「已收到请求但拒绝提供」,404 表示「未找到」。三者中只有 444 不会向对方返回任何内容。
具体区别有三点。第一,403 和 404 都要走完整的响应流程,Nginx 需要构造状态行、响应头和 error page 内容;404 默认还带一个几百字节的 HTML 页面,高频爬虫一天请求 10 万次就是几十 MB 的出网流量,而 444 的 $body_bytes_sent 恒为 0。第二,444 不给爬虫任何反馈信号——返回 403 相当于告诉对方「这个路径存在但被拒了」,返回 404 会诱导爬虫做更多探测,而直接断连会让部分低质量爬虫判定为网络故障并放弃。第三,访问日志里 444 仍会正常记录成 "GET / HTTP/1.1" 444 0,配合 $request_time 极短这一特征,便于事后统计哪些 UA 被拦得最多。
需要注意,444 是 Nginx 的非标准扩展,不是 HTTP 规范的一部分,Apache 和 Caddy 没有等价行为。
具体配置怎么写
结论:把 map 放在 http 块,把 return 444 放在 server 块,map 默认值写 0,命中的 UA 写 1。
第一步,在 nginx.conf 的 http { } 内定义匹配表。~* 表示不区分大小写的正则匹配,"" 单独对应空 User-Agent——相当一部分采集脚本根本不发 UA:
map $http_user_agent $bad_bot {
default 0;
"" 1; # 空 UA 直接拦
~*ahrefsbot 1;
~*semrushbot 1;
~*mj12bot 1;
~*dotbot 1;
~*petalbot 1;
~*bytespider 1;
~*gptbot 1;
~*ccbot 1;
~*claudebot 1;
}
第二步,在需要保护的 server 块里加判断。if 在 Nginx 里被称为「if is evil」,但 return 是 if 中少数完全安全的用法:
server {
listen 80;
server_name example.com;
if ($bad_bot) {
return 444;
}
location / {
proxy_pass http://127.0.0.1:8080;
}
}
如果只想拦某个目录,把 if ($bad_bot) { return 444; } 写进对应的 location 块即可,粒度比放在 server 层更细,不会影响静态资源和 API。
第三步,先测试再重载,不要直接 restart:
nginx -t # 输出 syntax is ok / test is successful 才能继续
nginx -s reload # 平滑重载,不断现有连接
怎么验证 444 真的生效了
结论:用 curl 伪造 UA 请求一次,如果返回 curl: (52) Empty reply from server 且退出码为 52,说明 444 已经生效。
验证命令:
curl -I -A "Mozilla/5.0 (compatible; AhrefsBot/7.0)" https://example.com
echo $? # 输出 52
curl -I -A "Mozilla/5.0 (compatible; Googlebot/2.1)" https://example.com
# 预期:正常的 HTTP/1.1 200 或 301/302
同时去 access.log 里确认,被拦的那条记录形如:
1.2.3.4 - - [10/May/2024:10:00:00 +0800] "GET / HTTP/1.1" 444 0 "-" "Mozilla/5.0 (compatible; AhrefsBot/7.0)"
444 后面的 0 就是发出的字节数,这一列全是 0 才算真正省下流量。
配置时最容易踩的三个坑
结论:误伤正规搜索引擎、把 map 放错位置、以为 444 能挡住改 UA 的爬虫,是这套配置最高频的三个问题。
第一,别把 Googlebot、Bingbot、Baiduspider 写进拦截表。 一旦误拦,站点在搜索结果里的抓取会直接停摆,恢复要等几天。如果确实想拦冒充 Googlebot 的请求,正确做法是反向 DNS 解析来源 IP,确认以 .googlebot.com 或 .google.com 结尾,而不是看 UA 字符串——UA 是可以随便伪造的。
第二,map 只能写在 http 块内,写进 server 或 location 会直接报 "map" directive is not allowed here,nginx -t 会立刻失败。改完配置记得先跑 nginx -t。
第三,444 只对「老实的爬虫」有效。 只要对方换一个普通浏览器 UA 就绕过了。想让拦截真正有效,要叠加多层:用 limit_req_zone 按 IP 限速(例如 limit_req zone=req_zone burst=20 nodelay;,配合 limit_req_zone $binary_remote_addr zone=req_zone:10m rate=5r/s;),用 deny 封禁已经确认的恶意 IP 段,再对异常高的 $request_time 或 404 比例做日志告警。444 只是第一道阻力较低的门槛,成本最低、见效最快,但不能指望它单独解决所有问题。
另外建议新配置先统一返回 403 观察 3 到 7 天日志,确认没有误伤正常来源后再切换成 444,切完对比 access.log 中 444 的条数占比,判断拦截比例是否在预期之内。
综上,拦爬虫的配置路径是:http 块里用 map $http_user_agent $bad_bot 建匹配表(含空 UA),server 或 location 块里 if ($bad_bot) { return 444; },nginx -t 通过后 nginx -s reload,用 curl -A 伪造 UA 验证返回 Empty reply 且日志字节数为 0。444 的价值在于零响应开销,代价是不给任何提示、也挡不住改 UA 的对手,必须和限速、IP 封禁一起用才构成完整的防护组合。
转载请注明出处,版权归原作者所有。