
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?id1到id50000掃一遍再把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 爬蟲在打 Bugzilla2.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?id1 HTTP/1.1 200 12345 - GPTBot/1.0 192.0.2.11 - - [14/May/2025:10:15:31 0000] GET /show_bug.cgi?id2 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.cgi、buglist.cgi、attachment.cgi等動態接口。短時間內訪問大量不同 ID例如從id1到id100000。單 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 中包含GPTBot、ClaudeBot等關鍵字的請求都會在進入后端之前直接返回 403。注意Nginx 的if指令放在location中可能產生意外行為尤其是使用proxy_pass時。如果需要按 User-Agent 拒絕請求盡量把if放在 server 上下文只做return 403不要在里面寫復雜邏輯。3.2 用 robots.txt 聲明抓取規則robots.txt 不是安全機制它只對“愿意遵守協議”的爬蟲有效。但它是成本最低的合規手段也能避免誤傷愿意協商的 AI 爬蟲。在站點根目錄放置 robots.txtUser-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 內觸發多少次后封禁5findtime統計窗口單位秒60bantime封禁時長單位秒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_zone和limit_req實現limit_req_zone $binary_remote_addr zonebugzilla_req:10m rate30r/m; server { listen 80; server_name bugs.example.org; location / { limit_req zonebugzilla_req burst20 nodelay; limit_req_status 429; proxy_pass http://127.0.0.1:8080; include proxy_params; } }這里有兩個關鍵參數rate30r/m每個 IP 平均每分鐘最多 30 個請求。burst20允許瞬間超過平均速率 20 個請求超過后進入排隊。nodelay突發請求不延遲處理但超過 burst 的部分直接返回 429。對于 Bugzilla 這類系統正常開發者每分鐘不會發起超過 30 個頁面請求。如果讀者的社區規模較小還可以把速率調低到10r/m。使用限流時要留意一個常見問題企業內部 NAT 出口可能讓很多用戶共享同一個公網 IP。如果這個出口的請求量超過了速率上限會導致正常用戶被 429。此時可以在測試環境先觀察再結合geo或map對可信網段放行。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 THRESHOLD1000 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 adminexample.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_agentlog_format ai_protect $remote_addr $request $status $http_user_agent;然后在需要分析的 server 中單獨配置 access_log統計時按這個格式切分即可。5.4 fail2ban 重啟后封禁消失現象重啟服務器后之前封禁的爬蟲 IP 又能訪問了。原因fail2ban 的封禁規則保存在內存中重啟后需要重新讀取日志并積累觸發次數如果系統沒有配置防火墻規則持久化也可能丟失。處理方式確認 fail2ban 服務已設置開機自啟并檢查iptables或nftables規則是否持久化。與安全相關的封禁本身有時間屬性短暫失效后如果爬蟲繼續產生異常日志fail2ban 會再次封禁。5.5 排查順序推薦遇到 Web 服務被爬蟲打掛不要先急著加規則按以下順序排查確認服務當前狀態CPU、內存、數據庫連接、磁盤是否異常。從 access log 看請求量最高的 UA、IP、URL。從 error log 看是否有連接超時、后端錯誤。確認日志記錄時間與服務器時區避免統計窗口錯亂。決定優先攔截點已知 UA 用 Nginx 攔截單 IP 異常用 fail2ban整體請求量大用限流。上線規則后再次統計同一指標驗證是否恢復正常。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 統計命令開始半天內就能建立起基本防線。