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

Nginx 用 map 指令屏蔽恶意爬虫的写法。

域名注册
域名注册 版主萌新站长 👑黑卡会员
发布于 2026-10-19 23:26 ·9 浏览 ·0 回复
内容摘要

用 http 块中的 map 将 $http_user_agent 映射为 0/1 变量,并在 server 块中以 if 返回 403,实现整站统一拦截恶意爬虫;变量只计算一次且可入日志,正则用 ~ 并排除合法爬虫以避免误封。

用 Nginx 屏蔽恶意爬虫最省事的写法是:在 http 块里用 map 把请求头 $http_user_agent 映射成一个 0/1 变量,再在 server 块里用 if ($变量) { return 403; } 统一拦截。整站只需计算一次变量,所有 location 复用同一份结果,比在每个 location 里重复写 if ($http_user_agent ~* ...) 更清晰,性能也更好。

map 屏蔽爬虫的完整配置怎么写

完整可复制的写法如下,map 必须放在 http 块内、server 块之外:

http {
    map $http_user_agent $bad_bot {
        default                                              0;
        ""                                                   1;
        ~*(Bytespider|SemrushBot|AhrefsBot|MJ12bot|DotBot)   1;
        ~*(python-requests|Go-http-client|Scrapy|curl/)      1;
    }

    server {
        listen 80;
        server_name example.com;

        if ($bad_bot) {
            return 403;
        }
        # 其余 location 配置
    }
}

改完执行 nginx -t 校验语法,通过后 nginx -s reload 平滑生效,老连接不断开。default 0 表示没命中的 UA 一律放行,这一行必须写,否则没命中时会得到空字符串。

为什么用 map 而不是在每个 location 写 if

结论:map 的匹配结果在请求进入时计算一次并缓存,后续所有 if、log_format、limit_req 都直接读取这个变量,而 if ($http_user_agent ~* ...) 写在多个 location 里会重复执行正则。

map 内部实现是哈希表加正则列表:精确字符串(不带 ~、~*)走哈希,O(1) 命中;正则按书写顺序逐个尝试,第一个命中的生效。所以把最常见的恶意 UA 写成精确字符串、把模糊匹配放后面,能省正则开销。

另外一个实际好处是变量可以进日志。在 log_format 里加一个 $bad_bot 字段,就能统计每天被拦了多少请求:

log_format main '$remote_addr - [$time_local] "$request" '
                '$status ua_block=$bad_bot "$http_user_agent"';

先观察一周日志再决定封哪些,比凭感觉拍脑袋加正则稳。

UA 正则怎么写才不误封

结论:必须用 ~* 做大小写不敏感匹配,并且把合法爬虫显式排除,否则容易把搜索引擎一起封掉。

三个要点:

  1. ~* 是大小写不敏感的正则前缀,~ 是大小写敏感。爬虫 UA 大小写经常变(Bytespider / bytespider),一律用 ~*。
  2. 别用过于宽泛的关键词。写 bot 会把 Googlebot、Bingbot 一起封掉;要封就封具体名字。
  3. UA 为空通常是扫描器或伪造请求,"" 1; 这一条能拦掉相当一部分无效流量,但部分老旧客户端(如某些 SDK、监控探针)也会带空 UA,上线前先用日志确认。

如果只想降低频率而不是直接拒绝,把 return 403 换成配合 limit_req 限速更合适:map 里返回 1 表示恶意、0 表示正常,再用 limit_req_zone $bad_bot zone=bot:10m rate=1r/s; 按变量分桶限速。

return 403 和 return 444 该选哪个

结论:对明确的恶意爬虫用 return 444,对普通访客可见的拦截用 return 403。

444 是 Nginx 自定义的非标准状态码,含义是直接关闭连接、不回任何响应。爬虫拿不到状态码和响应体,抓取成本更高,也不会在你的访问日志里刷出一堆 403。403 会正常返回一个错误页,适合“这个 IP/UA 确实不该访问,但需要留痕”的场景。

注意 if 在 server 块里只放 return 和 rewrite 是安全的,这是官方文档明确认可的两个用法;不要在 if 里写 proxy_pass、add_header 这类指令,会遇到 Nginx 著名的 “if is evil” 问题。

怎么验证屏蔽是否生效

用 curl 指定 UA 直接测,两条命令对比:

curl -I -A "Bytespider" https://example.com        # 期望 403 或连接被关闭
curl -I -A "Mozilla/5.0 (compatible; Googlebot/2.1)" https://example.com  # 期望 200

如果返回 403 但页面仍然被抓,检查两点:一是 map 是否真的写在 http 块里(写在 server 里会直接报 "map" directive is not allowed here);二是 CDN 或负载均衡是否改写了 UA,如果前面挂了 CDN,Nginx 拿到的 $http_user_agent 是 CDN 透传的值,需要在 CDN 层同步配置。

整套方案的核心就三步:map 定义 UA 到 0/1 的映射,server 块用 if + return 拦截,日志加变量做灰度验证。配置量不到 10 行,命中判断只执行一次,是性价比最高的反爬第一道防线。

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

全部回复 0

还没有回复,来抢沙发~