AI爬虫打挂Bugzilla?Nginx+fail2ban+限流实战防护指南
2026/9/6 1:54:23 网站建设 项目流程

Gentoo 项目的 Bugzilla 实例因为 AI bot scraper 流量过载而被迫限制访问,这类事件已经不是孤立案例。任何还在用传统 Nginx 访问日志、默认 robots.txt、不做限流的开源基础设施,都可能在某一天早上收到“页面打不开”的告警。AI 爬虫的抓取方式与普通搜索引擎差别很大:请求频率更高、遍历路径更广、很多还会带上查询参数去翻动态页面。问题一旦出现,通常不是加几台机器就能解决,而是要先把“是谁在打、打的是什么、为什么打挂”这条链路理清楚。

这篇文章从 Gentoo Bugzilla 事件出发,围绕一个自建 Web 服务如何抵御 AI 爬虫洪峰,整理出一套可以落地的思路:先识别流量,再在入口拦截,然后做动态封禁和应用层限流,最后通过日志和指标验证效果。整个过程不依赖商业产品,使用 Nginx、fail2ban、robots.txt 和日志分析就能完成大部分工作。适合正在维护开源项目站点、自建 Bugzilla 或其他 Web 应用的开发者参考。

1. 从 Bugzilla 过载看 AI 爬虫流量问题的本质

1.1 Bugzilla 为什么容易成为 AI 爬虫的受害者

Bugzilla 是很多开源项目使用的缺陷跟踪系统,页面大多是动态生成的。用户访问一个 bug 详情、搜索一个关键字、查看附件列表,都会触发后端查询数据库并渲染 HTML。这种“每页都在干活”的应用,天然对请求量非常敏感。

日常使用中,正常开发者提交 bug、补充注释、上传附件的频率不高,服务器压力有限。但 AI 爬虫不会按照人的节奏来。它们会从公开入口开始,顺着所有链接不断抓取,把show_bug.cgi?id=1id=50000扫一遍,再把buglist.cgi?quicksearch=...的各种查询组合都试一遍。这些请求会持续占用数据库连接、后端进程和带宽,最终导致正常用户无法访问。

从维护者角度看,麻烦的不只是“某个 IP 请求量大”,而是流量来源分散、User-Agent 伪装情况多、单次请求看起来很像正常浏览器。如果没有提前做监控和限流,问题往往要等到服务不可用或者数据库连接池耗尽时才会暴露。

1.2 AI 爬虫和传统搜索引擎爬虫的差异

搜索引擎爬虫并不是新鲜事物。但传统爬虫通常有明确的 User-Agent 标识,会遵守 robots.txt,抓取频率也相对克制。更重要的是,传统搜索引擎的目的是收录页面,不会为了一个搜索结果无限制地组合 URL。

AI 爬虫则不同:

维度传统搜索引擎爬虫部分 AI 爬虫
User-Agent标识稳定,容易识别可能标识稳定,也可能伪装成浏览器
robots.txt多数会遵守部分遵守,部分忽略
请求频率相对低可能极高,甚至并发抓取
抓取范围按入口和链接发现页面可能遍历动态参数、构造查询
对动态站点的压力中等高,因为每个请求都会触发服务端处理

这不是说所有 AI 爬虫都是恶意的,而是说它们的抓取策略以“拿到足够多语料”为目标,并不会考虑源站资源压力。对于 Bugzilla 这类动态查询系统,压力会被明显放大。

1.3 过载事故中受影响最严重的部分

一次 AI 爬虫引发的过载,最先出问题的往往不是入口网络,而是下面几个环节:

  • Web 服务器连接数满:大量并发请求占满 Nginx worker。
  • 数据库资源紧张:每次页面渲染都执行 SQL,数据库连接池耗尽。
  • 日志和磁盘压力:请求量暴增后 access log 写入变快,磁盘占用上升。
  • 正常用户体验恶化:页面响应时间从几百毫秒变成几十秒,甚至直接超时。

理解这些受影响环节,是为了确定防护重点。只加带宽没有用,因为压力在后端;只封几个 IP 也没有用,因为 AI 爬虫可能来自大量 IP。需要从入口到应用层做多层防护。

2. 先定位流量:如何确认是 AI 爬虫在打 Bugzilla

2.1 从访问日志入手

不要凭感觉判断流量来源,先打开访问日志,看真实请求。很多情况下,AI 爬虫的 User-Agent 会直接出现在日志里。以 Nginx 默认的 combined 格式为例:

sudo tail -f /var/log/nginx/bugzilla.access.log

日志中每一行大致是:

192.0.2.10 - - [14/May/2025:10:15:30 +0000] "GET /show_bug.cgi?id=1 HTTP/1.1" 200 12345 "-" "GPTBot/1.0" 192.0.2.11 - - [14/May/2025:10:15:31 +0000] "GET /show_bug.cgi?id=2 HTTP/1.1" 200 12350 "-" "ClaudeBot/1.0"

看到大量同一类 User-Agent 连续访问不同 bug id,基本可以确定是爬虫在批量抓取。

2.2 统计 User-Agent 分布

手动 tail 只能看几行,要判断整体情况,需要对历史日志做统计。Nginx combined 格式中,最后一个双引号字段是 User-Agent。如果日志格式未做特殊改动,可以用 awk 按双引号切分后提取第 6 个字段:

sudo awk -F'"' '{print $6}' /var/log/nginx/bugzilla.access.log | sort | uniq -c | sort -rn | head -20

输出示例:

15230 GPTBot/1.0 12110 ClaudeBot/1.0 8800 Bytespider 21 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...

如果某些浏览器 User-Agent 的请求数量也异常高,还要进一步看请求路径和频率,因为有些爬虫会伪装成浏览器。

2.3 分析请求行为特征

只统计 User-Agent 还不足以覆盖伪装场景。更可靠的方式是结合请求行为判断。以下特征组合起来,说明某类流量很可能是 AI 爬虫:

  • 请求全部是 GET,没有登录、提交表单等完整用户行为。
  • 请求路径集中在show_bug.cgibuglist.cgiattachment.cgi等动态接口。
  • 短时间内访问大量不同 ID,例如从id=1id=100000
  • 单 IP 请求速率远高于人类操作速度。
  • 请求之间没有思考间隔,不加载图片、CSS 等静态资源。
  • Referer 为空或来自固定入口页面。

建议写一个小脚本,按 IP 和 User-Agent 维度统计请求量:

sudo awk -F'"' '{print $1, $6}' /var/log/nginx/bugzilla.access.log \ | sed 's/ - - .*GET/ GET/' \ | sort | uniq -c | sort -rn | head -30

日志格式不同,字段切分方式需要相应调整。统计时要特别注意:不要只看总请求量,还要看同一 IP 在相同时间段内对动态 URL 的请求次数。

2.4 识别常见的 AI 爬虫标识

下面表格汇总了常见 AI 爬虫的 User-Agent 关键字。不同爬虫的标识可能随版本变化,当前信息以各家官方公开文档为准:

关键字示例通常关联的爬虫
GPTBotOpenAI 抓取工具
ChatGPT-UserOpenAI 交互类爬虫
ClaudeBotAnthropic 抓取工具
Claude-UserAnthropic 相关客户端
Google-ExtendedGoogle 的 AI 训练数据抓取声明
Bytespider字节跳动系爬虫
CCBotCommon Crawl 爬虫
PerplexityBotPerplexity 爬虫
AmazonbotAmazon 爬虫
GrokBotxAI 抓取工具

需要注意的是,User-Agent 黑名单只是基线,不能作为唯一防线。部分爬虫会伪造浏览器标识,也有新爬虫不断出现。识别阶段的目标是“找出大多数已知流量”,而不是做到 100%。

3. 分层次拦截与限流:从入口到应用层的保护方案

3.1 在 Nginx 入口拦截常见 AI 爬虫

最直接有效的拦截位置是 Nginx。在http块中定义一段map,把匹配到的 User-Agent 映射为拒绝标记:

map $http_user_agent $ai_scraper { default 0; "~*GPTBot" 1; "~*ClaudeBot" 1; "~*GrokBot" 1; "~*Bytespider" 1; "~*CCBot" 1; "~*PerplexityBot" 1; "~*Amazonbot" 1; "~*Google-Extended" 1; }

然后在 server 块中处理:

server { listen 80; server_name bugs.example.org; if ($ai_scraper) { return 403; } location / { proxy_pass http://127.0.0.1:8080; include proxy_params; } }

这里使用了正则匹配,~*表示不区分大小写。所有 User-Agent 中包含GPTBotClaudeBot等关键字的请求,都会在进入后端之前直接返回 403。

注意:Nginx 的if指令放在location中可能产生意外行为,尤其是使用proxy_pass时。如果需要按 User-Agent 拒绝请求,尽量把if放在 server 上下文,只做return 403,不要在里面写复杂逻辑。

3.2 用 robots.txt 声明抓取规则

robots.txt 不是安全机制,它只对“愿意遵守协议”的爬虫有效。但它是成本最低的合规手段,也能避免误伤愿意协商的 AI 爬虫。在站点根目录放置 robots.txt:

User-agent: * Allow: / User-agent: GPTBot Disallow: / User-agent: ClaudeBot Disallow: / User-agent: GrokBot Disallow: / User-agent: Bytespider Disallow: / User-agent: CCBot Disallow: / User-agent: PerplexityBot Disallow: / User-agent: Amazonbot Disallow: /

在这个配置中,普通搜索引擎仍然可以抓取,常见 AI 爬虫被明确禁止。很多知名爬虫会定期读取 robots.txt,因此即使 Nginx 拦截已经生效,也建议保留这份声明。

注意:robots.txt 不能替代访问控制。如果一个爬虫不遵守该协议,同时伪装成浏览器,那它仍会继续请求。不要因为加了 robots.txt 就降低其他防护。

3.3 用 fail2ban 做动态封禁

已知 User-Agent 黑名单无法应对伪装场景。当一段时间内某个 IP 频繁访问动态 URL 时,更适合用 fail2ban 按照 IP 维度做动态封禁。

创建过滤器/etc/fail2ban/filter.d/nginx-ai-scraper.conf

[Definition] failregex = ^<HOST> .*"(?:GET|POST|HEAD) .*" (?:403|404|429) .*"(?:GPTBot|ClaudeBot|GrokBot|Bytespider|CCBot|PerplexityBot|Amazonbot)" ignoreregex =

然后创建 jail 配置/etc/fail2ban/jail.d/bugzilla-ai.conf

[nginx-ai-scraper] enabled = true filter = nginx-ai-scraper logpath = /var/log/nginx/bugzilla.access.log maxretry = 5 findtime = 60 bantime = 3600

参数含义:

参数含义推荐值
maxretry在 findtime 内触发多少次后封禁5
findtime统计窗口,单位秒60
bantime封禁时长,单位秒3600 或更长
logpath需要检测的日志文件按实际路径填写

配置好之后启动并查看状态:

sudo systemctl restart fail2ban sudo fail2ban-client status sudo fail2ban-client status nginx-ai-scraper

如果看到 banned IP 列表,说明该 IP 已经触发阈值。fail2ban 底层通过防火墙规则丢弃来自这些 IP 的包,因此请求不会到达 Nginx,能显著降低后端压力。

3.4 对 Bugzilla 应用层做基础限流

入口拦截解决的是已知爬虫,fail2ban 解决的是明显异常的单个 IP。但 AI 爬虫可能使用大量不同 IP,单看某个 IP 可能并不超标,总体请求量却仍然很高。这时候需要加一层“每 IP 速率限制”。

在 Nginx 中,可以使用limit_req_zonelimit_req实现:

limit_req_zone $binary_remote_addr zone=bugzilla_req:10m rate=30r/m; server { listen 80; server_name bugs.example.org; location / { limit_req zone=bugzilla_req burst=20 nodelay; limit_req_status 429; proxy_pass http://127.0.0.1:8080; include proxy_params; } }

这里有两个关键参数:

  • rate=30r/m:每个 IP 平均每分钟最多 30 个请求。
  • burst=20:允许瞬间超过平均速率 20 个请求,超过后进入排队。
  • nodelay:突发请求不延迟处理,但超过 burst 的部分直接返回 429。

对于 Bugzilla 这类系统,正常开发者每分钟不会发起超过 30 个页面请求。如果读者的社区规模较小,还可以把速率调低到10r/m

使用限流时要留意一个常见问题:企业内部 NAT 出口可能让很多用户共享同一个公网 IP。如果这个出口的请求量超过了速率上限,会导致正常用户被 429。此时可以在测试环境先观察,再结合geomap对可信网段放行。

3.5 引入更严格的边缘防护

如果站点使用了云厂商的 CDN、防火墙或负载均衡,可以在边缘配置托管质询、WAF 规则或速率限制。这类方案通常能提供更细粒度的人工验证,比如浏览器自动通过 JavaScript 质询,而爬虫无法执行完整的浏览器环境。

具体配置因厂商而异,不在这里展开。使用原则是:边缘优先拦截大流量攻击,源站负责兜底;边缘限流规则要和 Nginx 的限流规则保持协同,避免边缘放行后源站仍然过载。

4. 验证防护效果:日志、指标与用户影响评估

4.1 确认拦截请求命中

配置完成后,先用模拟请求验证 Nginx 是否按预期工作:

# 模拟已知 AI 爬虫 curl -I -A "GPTBot/1.0" https://bugs.example.org/ # 模拟普通浏览器 curl -I -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" https://bugs.example.org/

预期结果:

  • 使用 GPTBot User-Agent 的请求返回 403。
  • 使用普通浏览器 UA 的请求返回 200 或 302(取决于 Bugzilla 是否需要登录)。

如果要看状态码,可以简化输出:

curl -o /dev/null -s -w "%{http_code}\n" -A "GPTBot/1.0" https://bugs.example.org/

输出403表示拦截生效。

4.2 对比请求量和资源占用

防护效果不能只看“403 有没有出现”,还要看整体流量是否下降。比较规则上线前后的访问日志:

sudo awk -F'"' '{print $6}' /var/log/nginx/bugzilla.access.log | sort | uniq -c | sort -rn | head -20

如果 GPTBot 等已知爬虫的请求量大幅下降,说明入口拦截有效。如果仍然很高,需要检查日志里记录的是拦截后返回 403 的请求还是进入后端的请求。返回 403 的请求也写 access log,但后端压力已经消失,所以不要看到日志里还有爬虫名字就认为拦截无效。

同时关注系统指标:

top free -h df -h

重点观察数据库连接数、PHP-FPM 或后端进程数、CPU 使用率和磁盘占用。正常状态下这些指标应当回落到爬虫爆发之前的水位。

4.3 检查正常用户是否受影响

防护规则上线后,最怕误伤正常用户。观察状态码分布是一个快速方法。在 Nginx 默认日志格式中,第 9 个字段是状态码:

sudo awk '{print $9}' /var/log/nginx/bugzilla.access.log | sort | uniq -c | sort -rn | head -20

如果 403 或 429 占比过高,可能说明规则过严。 403 不一定都是误伤,但要确认被 403 的请求里有没有大量正常浏览器 UA。正常情况下,200、301、302、404 占绝大多数,403/429 应该是少数。

4.4 建立简单告警

防住一次不等于永远安全。可以写一个简单脚本,每天统计日志中 AI 爬虫请求量,并设置阈值告警:

#!/bin/bash LOG=/var/log/nginx/bugzilla.access.log THRESHOLD=1000 COUNT=$(grep -cE "GPTBot|ClaudeBot|GrokBot|Bytespider|CCBot|PerplexityBot|Amazonbot" "$LOG") if [ "$COUNT" -gt "$THRESHOLD" ]; then echo "AI scraper request count in last log period: $COUNT" | mail -s "AI scraper alert" admin@example.com fi

这个脚本只是演示。实际生产环境建议让日志进入集中采集系统,再结合 Prometheus、Loki 或 Elasticsearch 做自动告警。告警阈值不是越高越好,要根据站点正常请求量确定,否则要么天天误报,要么真出事时没有通知。

5. 常见坑与排查链路

5.1 只做 User-Agent 黑名单,爬虫换 UA 后就失效

现象:最开始封了一批 GPTBot 请求,流量下降明显,几天后流量又回到高位。查询日志发现大量“Mozilla/5.0”请求。原因:有些爬虫会伪装成浏览器或定期切换 UA。

处理方式:把 User-Agent 黑名单当作“基础过滤”,同时启用 fail2ban 和 Nginx 速率限制。封禁的维度从“UA 关键词”迁移到“IP + 行为”。如果请求来自大量 IP,则在应用层增加验证码或托管质询。

5.2 正则写得太宽,误杀正常用户

现象:某些安装软件、SDK 或普通浏览器的请求被 403。原因:正则匹配了bot这个通用词,比如把"SomeBot"也当作 AI 爬虫;或者网站本身存在名称包含 bot 的模块。

处理方式:使用完整的已知爬虫关键词,不要写"~*bot"这种过宽规则。在 map 中的正则尽量用具体名称,上线前用一组正常 UA 做回归测试。

5.3 日志字段切分错误,统计结果不准

现象:统计 User-Agent 时输出为空,或者把 IP 当成了 UA。原因:Nginx 的log_format不是默认格式,字段顺序发生了变化。比如把 Referer 和 User-Agent 的位置换了,或加了额外字段。

处理方式:先看 Nginx 配置中log_format的定义。如果不确定字段位置,可以临时加一个专门的日志格式,只输出$remote_addr$request$status$http_user_agent

log_format ai_protect '$remote_addr "$request" $status "$http_user_agent"';

然后在需要分析的 server 中单独配置 access_log,统计时按这个格式切分即可。

5.4 fail2ban 重启后封禁消失

现象:重启服务器后,之前封禁的爬虫 IP 又能访问了。原因:fail2ban 的封禁规则保存在内存中,重启后需要重新读取日志并积累触发次数;如果系统没有配置防火墙规则持久化,也可能丢失。

处理方式:确认 fail2ban 服务已设置开机自启,并检查iptablesnftables规则是否持久化。与安全相关的封禁本身有时间属性,短暂失效后如果爬虫继续产生异常日志,fail2ban 会再次封禁。

5.5 排查顺序推荐

遇到 Web 服务被爬虫打挂,不要先急着加规则,按以下顺序排查:

  1. 确认服务当前状态:CPU、内存、数据库连接、磁盘是否异常。
  2. 从 access log 看请求量最高的 UA、IP、URL。
  3. 从 error log 看是否有连接超时、后端错误。
  4. 确认日志记录时间与服务器时区,避免统计窗口错乱。
  5. 决定优先拦截点:已知 UA 用 Nginx 拦截,单 IP 异常用 fail2ban,整体请求量大用限流。
  6. 上线规则后再次统计同一指标,验证是否恢复正常。

6. 生产环境的长期方案

6.1 基础设施层:不要把压力都留给源站

Nginx 的 UA 拦截和限流能解决很多问题,但面对大规模分散爬虫流量时,源站仍然可能被高并发打满。更稳妥的做法是引入边缘缓存和边缘防护。

静态资源可以通过 CDN 缓存,减少源站请求。动态页面虽然不能全部缓存,但可以在边缘做质询和速率限制。这样即使爬虫来自成千上万个 IP,源站也只处理真正通过边缘校验的请求。

架构调整可以分三步走:

  • 第一步:在 Nginx 层完成 UA 黑名单和 IP 限流。
  • 第二步:把 fail2ban、访问日志监控纳入日常运维。
  • 第三步:根据流量变化评估是否需要边缘防护和托管质询。

6.2 应用层:保护动态接口和数据库

对 Bugzilla 这类动态应用,建议关注查询接口的资源消耗。常用做法包括:

  • 给热点用户页面加缓存,减少重复渲染。
  • 限制历史 bug 数据的深度遍历,比如对附件、全文检索接口做单独限流。
  • 对需要登录才能访问的接口,强制会话校验。
  • 对公开搜索接口增加分页大小限制,避免单次请求查询范围过大。
  • 在数据库层设置连接池上限和慢查询阈值,防止单类查询拖垮整个实例。

这些优化不直接针对 AI 爬虫,但能显著提高系统的抗压能力。出现过载时,后端越“轻”,防护规则越容易生效。

6.3 治理层面:维护一个 AI 爬虫清单

AI 爬虫列表会不断变化,维护者可以建立一份内部清单,记录启动时间、User-Agent、访问范围、是否遵守 robots.txt 等信息。这不仅能帮助快速封禁,也能在未来评估某个爬虫是否符合站点政策。

清单建议包含以下字段:

name: GPTBot user_agent: GPTBot official_docs: https://openai.com/gptbot respects_robots: true/unknown/false observed_at: 2025-05-14 notes: 曾批量抓取 show_bug.cgi

用 YAML、JSON 或表格维护都可以。关键是让团队在遇到“新 UA 请求量高”时,有一个快速查询和沉淀答案的地方。

6.4 开源项目维护者还可以做什么

开源基础设施的维护者通常没有专职安全团队,能做的更多是提前准备:

  • 开启 Web 访问日志的轮转,防止磁盘被日志打满。
  • 定期复盘访问日志中的异常流量。
  • 在官方站点放置明确的抓取政策。
  • 与大型 AI 公司约定抓取配额,很多厂商提供 robots.txt 或单独的抓取协议入口。
  • 重要数据提供离线打包下载,降低按页面抓取的频率。

开源项目的特点决定了站点很难完全封闭。与其被动等下一次爬虫把站点打挂,不如把可观测性做好,把拦截规则提前放在线上。

到这里,这套从日志分析到 Nginx 拦截,再到 fail2ban 动态封禁、应用层限流和效果验证的方法,就可以在真实基础设施上落地了。核心判断是:AI 爬虫流量不会消失,只会越来越多;只靠某一种手段无法长期有效,必须形成“识别、拦截、限流、监控、复盘”的循环。对于正在维护 Bugzilla 或其他动态站点的开发者,建议先从修改 Nginx 配置和写一个 UA 统计命令开始,半天内就能建立起基本防线。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询