⭐ 推荐:社区规则条款 V1.0

如何用 Nginx 的 rewrite 规则屏蔽特定 URL 的爬虫?

GJ论坛站长
GJ论坛站长 管理员资深会员 👑黑卡会员
发布于 2026-10-22 09:02 ·10 浏览 ·0 回复
内容摘要

用 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;
        }
        # 该路径的正常业务配置
    }
}

三个要点:

  1. location ~* 是大小写不敏感的正则匹配,优先级高于普通前缀匹配;若希望某个目录整段跳过正则,改写为 location ^~ /static/。
  2. if 块内只放 return、rewrite、set、add_header 这几类简单指令是安全的;一旦把 proxy_pass、fastcgi_pass 写进 if,就落入官方文档标注的 if is evil 未定义行为,表现为部分请求跳过反代或直接 502。需要反代时,把 if ($bad_bot) { return 403; } 放在 location 顶层,proxy_pass 放在它后面。
  3. 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 兜底。

本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2374.html
转载请注明出处,版权归原作者所有。
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 0

还没有回复,来抢沙发~