AI爬虫过载致Bugzilla关闭:动态网站防爬限流与缓存策略解析

发布时间:2026/8/28 13:27:39
AI爬虫过载致Bugzilla关闭:动态网站防爬限流与缓存策略解析 Gentoo 的 Bugzilla 因为 AI 机器人爬虫过载而关闭入口这则消息在开源社区里传开后很多自建站点的维护者都在讨论同一个问题如果连老牌发行版的基础设施都会被爬虫流量打垮我们自己的工单系统、Wiki、论坛是不是也站在同样的风险里。Bugzilla 是 Mozilla 和不少开源项目都在用的缺陷跟踪系统传统上靠登录、搜索、邮件通知和数据库查询组织流程。AI 爬虫一旦批量调用这些动态页面负载会迅速被放大。这篇文章不是来复述新闻的我想拆一下现象背后的技术原因再讲一套从排查到防护的实操路径。1. 这次事件暴露的不是单一站点问题而是公共服务基础设施的普遍问题1.1 工单系统为什么会被“爬”到关闭Bugzilla 不是一个静态博客。它的页面大多数从数据库实时生成列表页要查缺陷、筛选条件、用户、附件、评论搜索页可能触发更复杂的 SQL登录状态、CSRF 令牌、邮件通知又让每次会话多出额外开销。正常用户访问量不高时这套机制没问题但爬虫访问方式完全不同。普通浏览器用户会先打开首页再看一条缺陷阅读一段时间再点下一个链接爬虫则可能同时发起几十上百个请求沿着列表、分页、筛选条件递归抓取。一个请求加上一次数据库查询负载立刻显现。如果维护者没有在入口层做限制爬虫可以在几小时里把数据库连接数打满。问题不在于“一两个请求很贵”而在于“同一类请求被无限放大”。Bugzilla 的buglist.cgi、show_bug.cgi、query.cgi都是典型动态入口每一个都可能触发权限判断、SQL 查询和模板渲染。这类页面按顺序访问看不出风险一旦变成并发抓取后端压力是成倍增长的。1.2 被影响的不仅是 CPU还有数据库和会话层具体来看CPU 会先飙高紧接着数据库连接数不够用慢查询变多如果系统还在做邮件通知爬虫反复创建或更新一批缺陷还有触发邮件风暴的风险。Bugzilla 的 session 也会因为大量并发请求不断创建和清理。很多老系统没做读写分离也没接缓存所以每个动态请求都直接打到数据库。关闭入口是最快的止血方式但这是不得已的办法等于把正常用户也挡在外面。从影响范围看爬虫过载不只是让页面变慢。它会连带影响登录、搜索、附件上传、邮件通知、API 接口。用户看到的可能是“页面打不开”“提交工单超时”“登录后跳转失败”但背后的根因往往是同一个后端资源被非业务流量占满。遇到这种问题不要只盯着首页能不能打开要把整个链路都检查一遍。1.3 先想清楚问题出在“流量大”还是“请求方式差”我遇到类似情况时不会被“流量大”这句话带偏。流量大可以简单扩容但更常见的是请求方式差同一个 IP 反复抓分页、大量搜索、绕过 robots 抓不该抓的地址、甚至用随机参数不停刷新。这类请求对业务没有价值却占用真实资源。Gentoo 遇到的 AI 爬虫过载核心不在“AI”这个词而在于这些爬虫会按分析型方式遍历动态系统。所以后续所有策略都要围绕“区分真实用户和批量程序”来做而不是单纯调大并发上限。如果一个系统平时能扛住 500 个在线用户突然被 50 个爬虫实例打挂那不是业务量上来了而是请求模型发生了变化。判断问题性质比急着扩容更重要。2. AI 爬虫和传统搜索引擎爬虫不是一回事2.1 动态页面对爬虫放大了负载一个动态页面的 URL 看起来像一个普通地址但服务端要从数据库读取结果、渲染 HTML、处理权限判断。搜索引擎爬虫虽然也会抓很多页面但大多经过 robots 协议约束并且抓取节奏相对稳定。很多 AI 项目爬虫的目标不是“收录页面”而是搜集训练语料、截图、结构化数据所以行为更像扫描器会顺着分页、变化参数、跟踪所有可点击链接甚至把搜索功能当成数据接口。同一个时间几百个请求动态站点就很难扛。更需要注意的是现在不少 AI agent 会代替用户访问网站。比如某个工具需要查一个缺陷的状态它会尝试登录、搜索、遍历列表页。一次两次没问题一旦这类工具被大量用户使用服务端看到的就是一个持续不断的自动化流量。和传统爬虫相比这类流量的路径更多样也更难用一个固定 UA 识别。2.2 User-Agent 不再是可靠的身份凭证现在很多爬虫会带着正常浏览器的 User-Agent伪装成 Chrome 或 Safari。也有不少 AI 爬虫会明确标注自己例如 GPTBot、ClaudeBot、GrokBot 等。问题在于不能只靠 UA 做判断。一方面标注了的爬虫如果并发过高或路径不合理仍然需要控制另一方面伪装 UA 后识别会非常困难。所以我的经验是UA 只能作为第一条线索后面还要结合 IP、路径、频率和行为判断。只看 UA 还容易出现一个误区有些内部监控脚本也使用正常浏览器标识一看到来自某个机房的访问就当成爬虫封禁结果误伤了自己的监控系统。所以任何基于 UA 的拦截规则都要先统计一段时间内的请求分布确认规则影响面之后再上线。2.3 robots.txt 的失效与“君子协议”边界robots.txt 本质上是一个君子协议。传统搜索引擎大多会遵守但 AI 爬虫的语境里很多爬虫并不理会甚至认为抓取公开网页不需要征求同意。更重要的是robots.txt 只控制“该不该抓”不能限制“抓多快、怎么抓”。一个友好的爬虫即使被允许抓取也可能在短时间内发起大量请求。比如允许抓取/show_bug.cgi?id123爬虫完全可以快速递增id把一万条缺陷在几十分钟内抓完。所以不要只依赖 robots.txt 做防护一定要在 Web 服务器和业务层做实际限流。这里不是鼓励屏蔽所有 AI 爬虫而是要把协议约束和工程限制结合起来。真正健康的状态是允许有纪律的抓取拒绝无节制的并发遍历。3. 先判断是不是爬虫过载三条排查线3.1 日志聚合先看 UA再看 IP当系统开始慢不要急着改代码。先打开访问日志按 User-Agent 聚合看哪些标识占的比例异常高。比如日志里突然出现大量 GPTBot 或 DeepBot 之类的标识并且请求间隔极短基本可以判断有爬虫流量。如果 UA 正常再按客户端 IP 聚合看是否集中在某个网段、机房 IP 或数据中心段。注意有些爬虫会分布式部署IP 分散这时要按 URL 路径特征去统计。# 以 Nginx 默认日志格式为例统计访问量最高的 User-Agent awk -F {print $6} /var/log/nginx/access.log \ | sort | uniq -c | sort -rn | head -30如果日志格式不同字段位置会变。可以先执行tail -n 1 access.log查看实际字段顺序再用相应列。建议做统计时把健康检查、自己测试的请求过滤掉否则会被干扰。3.2 请求路径特征看它在抓什么爬虫的来源往往有规律。比如一个爬虫只抓/show_bug.cgi?id后面的所有编号并且一直递增或者在/buglist.cgi上反复提交相同查询或者请求大量不存在的附件、用户主页。打开日志后按 URL path 分组统计awk {print $7} /var/log/nginx/access.log \ | cut -d? -f1 \ | sort | uniq -c | sort -rn | head -30如果某个动态路径的比例异常高就说明爬虫在集中访问这个入口。这时可以进一步查看这些请求的 Referer、响应状态码和请求耗时。比如buglist.cgi占比从平时的 10% 涨到 60%同时大量请求返回 200 且耗时超过 2 秒基本能坐实是爬虫在抓搜索或列表接口。3.3 资源指标CPU 高不代表是爬虫要结合慢查询与会话数CPU 高也可能来自正常业务高峰。排查时要把系统指标和日志特征对齐CPU、load、内存、数据库连接数、慢查询数量、应用服务器线程数。如果数据库慢查询集中在某个 SELECT再去日志里找对应时间段看是不是同一个 IP 或 UA 在大量请求。这里最容易踩坑的是只看到 CPU 高就开始封 IP结果封掉的是 CDN 回源或正常用户代理出口。一定要先确认来源特征再做限制。另外Bugzilla 这类系统慢查询日志默认不一定开启。建议提前打开 MySQL 或 PostgreSQL 的慢查询记录并且把long_query_time设置到 1 秒甚至更短。没有慢查询日志事后很难还原当时的数据库压力来源。3.4 业务侧指标工单提交、搜索失败率同样重要从业务上看Bugzilla 这类系统还会出现搜索超时、登录页打不开、附件上传失败、邮件通知延迟。这些指标比 CPU 更能说明用户实际受影响程度。如果你维护的是自己的工单系统建议看几个核心指标登录成功率、列表接口响应时间、问题创建成功率、邮件队列长度。任何一个异常都先回到日志确认有没有被爬虫或者异常程序拖慢。一个实用技巧是提前做一个“关键路径请求耗时”监控。比如show_bug.cgi的 p95 响应时间正常时在 300 毫秒以内爬虫过载时可能超过 5 秒。有了这个基准下次系统卡顿时你就能快速判断是局部接口问题还是整体负载问题。4. 防护方案按从轻到重的顺序落地4.1 第一层规范爬虫协议并设置合理的 robots.txtrobots.txt 不能解决全部问题但它是成本最低的第一步。可以禁止不友好的爬虫访问动态页面例如User-agent: GPTBot Disallow: / User-agent: ClaudeBot Disallow: / User-agent: GrokBot Disallow: / User-agent: * Allow: / Disallow: /buglist.cgi Disallow: /query.cgi Disallow: /show_bug.cgi?formatmultiple同时建议在页面中给 AI 爬虫提供明确的抓取规则。但要注意robots.txt 对不遵守的爬虫无效所以这只是声明不是防护。4.2 第二层入口限流用 Nginx 对请求频率做硬限制即使爬虫 UA 合法过高的请求频率也要限制。Nginx 可以用limit_req做单 IP 或单 UA 的限流limit_req_zone $binary_remote_addr zonebugzilla_limit:10m rate10r/s; server { location /bugzilla/ { limit_req zonebugzilla_limit burst20 nodelay; # 其他配置 } }这里的rate10r/s只是示例实际值要根据正常用户访问频率定。如果站点有大量用户来自同一出口 IP例如公司内网单 IP 限流可能误伤所以可以用$http_user_agent作为 key 来限制同一爬虫标识。但 UA 可以伪造因此入口限流只是兜底不是全部。更稳妥的做法是限流只在检测到异常时启用默认放行。不要一上来就设很低的值否则正常用户会被挡。4.3 第三层fail2ban 和临时封禁针对已经识别到的爬虫如果日志里已经发现特定 IP 持续刷请求可以通过 fail2ban 做临时封禁。配置一般分两步写过滤器匹配日志中的 UA 或 Path 特征再配置动作封禁对应 IP。例如匹配日志中大量buglist.cgi请求的记录[definition] failregex .* GET /bugzilla/buglist\.cgi\?.* .* 200 ignoreregex 然后设置findtime、maxretry、bantime。使用 fail2ban 时我建议先用几小时日志调正则确认不会匹配到正常请求再启用。封禁时间从 10 分钟开始别一上来就封 24 小时。误封一个正常用户比放走一个爬虫更麻烦。4.4 第四层人机验证和 JS 挑战当限流仍挡不住时要考虑人机验证。常见的方式在登录入口、搜索页、工单创建页增加验证码。在 CDN 层开启 JS Challenge。对可疑 IP 返回一个需要执行 JavaScript 才能访问的中间页。验证码会增加正常用户成本所以不要放在所有页面只放在高频动态操作上。另外开源社区还要考虑无障碍辅助工具的兼容性验证方案不能把屏幕阅读器用户挡住。从实测效果看JS Challenge 对大部分爬虫更有效因为它要求客户端执行脚本很多简易爬虫没有这个能力。但遇到无头浏览器类爬虫时JS Challenge 也会被绕过所以它只能提高攻击成本不是绝对防线。4.5 防护等级选择参考防护等级适用情况正常用户影响维护成本robots.txt低风险用于声明基本无很低Nginx 限流出现异常请求但不确定来源较低需要调参数低fail2ban已确认特定 IP/UA 攻击低但要防误封中CDN JS 挑战高频攻击需要快速止血中部分用户会困惑中高验证码核心动态操作被滥用中高高这五层可以叠加。实际项目里我一般先用 robots.txt Nginx 限流守住第一关再对异常来源做 fail2ban。如果仍然扛不住再考虑 CDN 或验证码。5. 对开源项目和社区基础设施的长期建议5.1 把动态入口和只读内容分开Bugzilla 被爬虫打很大原因是很多本可以被静态化或走 API 的数据被爬虫当作 HTML 页面逐页抓取。项目维护者可以做一个友好的内容出口公开缺陷问题的关键信息通过只读 API 或定期生成的导出包提供。这样需要数据的人不用去抓动态页面维护方也能把请求限制在可管理的范围。Bugzilla 本身提供 REST API可以要求 API 调用方申请 key限制频率和用途。这个思路对任何开源项目都适用。Wiki 可以导出静态快照文档站点可以生成离线版本工单系统可以开放只读 API。只要数据需求被满足纯粹的页面抓取就少了理由。5.2 给“数据需求方”提供官方口子而不是情绪化封禁遇到 AI 爬虫很多团队第一反应是全封。但全封会误伤开发者、研究者、镜像站和合法工具。更温和也更容易操作的做法在站点明确标注哪些内容允许程序化访问把数据需求导向 API 或导出文件然后对不遵守规则的请求做限速。比如可以在页脚加一个“开发者访问方式”的链接说明 API 的注册方式和使用限制。能拿到官方数据的爬虫没有必要再硬抓页面不配合的爬虫则有了明确的拒绝理由。这里的关键是“给出口”和“立规矩”同时做。只封入口不给替代方案正常用户和开发者也会被卷进误伤范围只给 API 不做限制API 又可能被滥用。5.3 动态页面加缓存给数据库减负对 Bugzilla 这类系统最直接有效的优化是加页面缓存和数据库查询缓存。常见方案给未登录用户的公开缺陷页、列表页设置短缓存在应用层用 Redis 缓存用户权限和配置对热门搜索词做结果缓存。缓存命中高了之后即使爬虫请求量没有变少后端负载也会明显下降。我在实际操作中通常会先看动态请求里哪些 URL 占比最高然后先给 Top 10 路径加缓存见效最快。注意缓存不要太久否则工单状态更新后用户会看到旧数据。公开工单页可以设 30 到 60 秒缓存搜索页可以更短避免影响同步时效。5.4 准备一套可执行的应急预案Gentoo 的 Bugzilla 关闭入口说明即便是一个技术积淀很深的社区也需要在高压流量下做紧急决策。所以任何自建系统都建议提前写一份“爬虫过载应急预案”。内容包括保留几个负责人能随时改的 Nginx 配置片段。准备好限流开关和默认参数。明确哪些路径可以临时关停哪些必须保持可用。日志采集和分析的命令。一个可以联系到的 on-call 负责人。预案不是写文档而是定期演练。哪怕只是每个月把日志统计命令跑一遍都会比事发当天翻手册快很多。6. 复盘清单下次再遇到爬虫过载按这个顺序处理6.1 紧急止血先保业务再分析原因如果系统已经打不开或响应非常慢第一步不是写脚本分析日志而是先做止血。常见的止血操作有在入口层把可疑路径限流临时禁用搜索接口把动态页面切换成静态缓存页如果需要关掉对外服务并在首页放维护说明。止血时优先保证正常用户能访问哪怕功能临时简化。记录下此刻时间、改动内容和效果方便后面复盘。不要一边症状恶化一边去查日志那样容易被大量异常信息带偏。6.2 定位来源按 UA、IP、路径三层拉列表止血后按三层数据判断来源。先聚合 UA再看 IP最后看路径。如果 UA 和 IP 都分散就查路径特征往往会发现所有异常请求都集中在某一个动态接口上。把三层结果合并成一张表标记哪些流量需要限制、哪些是正常用户、哪些无法判断。这里要保留“未知”分类不要把所有异常都打成爬虫。有些是搜索引擎新爬虫有些是监控脚本有些是用户下载工具。6.3 分级处置拒绝、限速、挑战、缓存一起上处置不要只做一项。先对明确不友好的 IP/UA 拒绝访问然后对可疑流量限速再对高频动态路径做 JS 挑战或验证码同时对只读页面加缓存。四件事不是替代关系而是配合关系。拒绝名单能让绝大多数流量直接断开限速能控制剩余请求的速率验证码能拦住自动脚本缓存能保证正常用户遇到动态请求时后端不会被压垮。分级处置要在几小时内完成每步都观察对现有用户的影响。如果某项措施上线后正常用户投诉明显增多就要及时调整参数或回滚。6.4 长期优化把爬虫治理纳入运维日常爬虫治理不是一次性的战役。AI 项目越来越多爬虫标识和行为也会持续变化。建议每周或每月跑一次访问日志分析建立一个“异常请求来源”的基线出现新的 AI 爬虫标识时先观察几天再决定是否加入限制名单robots.txt 中维护一个定期更新的不友好爬虫列表。同时要定期检查限流参数避免业务增长后原来的 10r/s 已经影响真实用户。真正长期有效的方案不是把所有爬虫都封死而是建立一套能快速识别、分级处理、持续改进的机制。这次 Gentoo Bugzilla 被 AI 爬虫拖到关闭入口给所有维护者的提醒其实很直接动态网站只要公开就要默认它会被程序化访问。与其等系统被打挂再救火不如提前把协议、限流、缓存、API 和应急开关都准备好。我个人的原则很简单先用日志确认来源再分级处置能走 API 的数据就不让爬虫抓页面该拦截的拦截但永远留一条正常开发者入口。这样既能保护基础设施也不会把真实用户挡在门外。