iT邦幫忙

2026 iThome 鐵人賽

DAY 9
0
Modern Web

你的第一本 AEO x GEO 教戰手冊系列 第 9

robots.txt 明明放行了,Cloudflare 卻冷冷回個 403?揪出 WAF 與 Bot Fight Mode 的隱形暗礁

  • 分享至 

  • xImage
  •  

昨天 (Day 8) 我們寫的那支 Worker,其實藏了一個很殘酷的前提:它得先有命活到被執行。

你一定遇過這種見鬼的狀況。你的 robots.txt 明明白白寫著 Allow: /,AI Crawl Control 裡面也沒按過任何封鎖,但你就是覺得根本沒有 AI 爬蟲來光顧。或者更直白一點,你自己用 curl -A "GPTBot/1.0" 去敲自己的網站,結果當場吃了一個 403 閉門羹。

問題根本不在 robots.txt,也跟 AI Crawl Control 無關。兇手是同一個 zone 上面的另外幾個安全開關,在請求碰到你辛辛苦苦寫的自訂邏輯之前,就直接在門外把它給斃了。更可怕的是,在免費與 Pro 方案裡,這幾個殺手預設是醒著的

今天我們來面對現實。先把這幾個開關的底細摸清楚 (一切以官方文件為準),接著給你一套驗證手法,最後分享一個我們自己親身踩過兩次、摔得鼻青臉腫的判讀陷阱。那個陷阱曾經害我們對外發布過兩次錯誤的結論。

所有 Cloudflare 行為描述截至 2026-09-07,出處全在文末的 evidence 表。

一、這三個開關藏在哪?一分鐘幫你定位

我們先把地圖攤開來。它們全躲在 zone 的 Security -> Settings 裡面,只是被分發在不同的篩選標籤下:

開關 方案 預設 位置
Browser Integrity Check 所有方案 Security -> Settings (篩 DDoS attacks)
Bot Fight Mode 免費方案 需手動開 Security -> Settings (篩 Bot traffic)
Super Bot Fight Mode Pro / Business / Enterprise 需手動開 Security -> Settings (篩 Bot traffic)
Block AI bots 不適用 不適用 同上;2026-09-15 deprecate,這是 Day 10 的題目

Browser Integrity Check 那個「預設開」非常要命。官方文件只有冷冷一句:「Browser Integrity Check is enabled by default.」意思是只要你什麼都沒做,它就已經在暗處盯著你了。

二、這些保鑣私底下到底在幹嘛

Browser Integrity Check (BIC)

官方的描述有兩句,值得你一字不漏地看進去:

「Cloudflare's Browser Integrity Check (BIC) looks for common HTTP headers abused most commonly by spammers and denies access to your page.」
「It also challenges visitors without a user agent or with a non-standard user agent such as commonly used by abusive bots, crawlers, or visitors.」

看懂了嗎?它其實在幹兩件不同的事。遇到可疑的 header,它直接拒絕存取;遇到奇怪的 UA,它是發出挑戰。而「non-standard user agent」這個標準本身就非常曖昧。一個寫得規規矩矩的 GPTBot/1.2 到底算不算標準?文件裡根本沒有給你一個可以驗證的定義,這正是我們第四節要對付的麻煩。

Bot Fight Mode (免費方案)

「Bot Fight Mode is a simple, free product that helps detect and mitigate bot traffic on your domain.」

它負責辨識那些長得像已知機器人的流量,然後「issues computationally expensive challenges that force the requesting client to perform CPU-intensive calculations」。白話文就是:發出極度消耗運算資源的挑戰,逼著客戶端去跑 CPU 密集計算,藉此把自動化請求的成本拉高。

這裡還有一句藏在 Limitations (限制) 區塊裡的狠話,殺傷力極大:「For Bot Fight Mode customers, JavaScript Detections is automatically enabled and cannot be disabled.」 強制啟用 JavaScript 偵測,而且不准你關。請記住,爬蟲是不跑 JS 的。這是他們在設計上刻意注定的死局,絕對不是什麼意外。

Super Bot Fight Mode (Pro / Business / Enterprise)

「Super Bot Fight Mode is included in your Pro, Business, or Enterprise subscription. Compared to Bot Fight Mode, Super Bot Fight Mode adds configurable actions per bot category, bot analytics, and the ability to create exceptions using WAF custom rules.」

它把流量切成三塊:Definitely automated trafficLikely automated traffic,以及 Verified bots。最後那個是救命關鍵,已驗證機器人有自己獨立的設定格,你可以單獨對他們放行 (Allow)。

三、最殘酷的不對稱:誰能繞得過去,誰又是一堵死牆

這一節是今天最值錢的情報,因為網路上流傳的解法有一半根本是來亂的

你一定常看到那些英文 SEO 農場文教你:「只要寫一條 WAF custom rule,對 AI 爬蟲的 UA 設定 Skip,就能放行啦。」這個爛建議套在 Super Bot Fight Mode 上或許管用,但面對免費方案的 Bot Fight Mode 絕對無效。這不是「效果比較差」的問題,是「一點效果都沒有」。

官方文件講得很絕:

「You cannot bypass or skip Bot Fight Mode using WAF custom rules or Page Rules. This is because Bot Fight Mode does not run on the Ruleset Engine : it operates in a separate evaluation pipeline where Skip, Bypass, and Allow actions have no effect.」

幫你整理成一張表:

開關 WAF custom rule 的 Skip 有效嗎 官方依據
Browser Integrity Check ✅ 有效 (也可用 Configuration Rules 依路徑開關) 「skip Browser Integrity Check using a custom rule with a skip action」
Bot Fight Mode 無效 不跑在 Ruleset Engine 上,Skip / Bypass / Allow 都沒有效果
Super Bot Fight Mode ✅ 有效 「Custom rules are executed before Super Bot Fight Mode… create a custom rule with the Skip action」;它跑在 http_request_sbfm 這個 phase

所以,在免費方案上,面對 Bot Fight Mode 你只有三條殘酷的出路:

  1. 狠心把它關了。官方自己的建議就是:「If you need to create exceptions for specific traffic, use Super Bot Fight Mode instead.」說穿了就是要你掏錢升級。
  2. 利用 IP Access rules 搶在它動手前攔截。 這是文件裡唯一施捨給免費方案的例外機制:「Bot Fight Mode can still trigger if you have IP Access rules, but it will not trigger if an IP Access rule matches the request first。」實務上的做法是,你把各家 AI 爬蟲公開的官方 IP 清單,全部建進 IP Access 的 Allow 規則裡,讓他們在碰到 Bot Fight Mode 之前就被保送通關。維護這些名單很痛苦 (IP 會漂移,Day 4 我們算過這筆帳),但這是免費方案裡唯一真正有效的白名單玩法。
  3. 乖乖升級到 Pro,用 Super Bot Fight Mode 把 Verified bots 設成 Allow。

如果你在免費方案上寫了一條 WAF skip rule 就滿心以為天下太平,那你只是踩進了最常見也最浪費時間的坑。規則表面上會顯示「已啟用」,Security Events 裡也看得到它被觸發,但爬蟲照樣在門外被宰了。

四、這不是無差別大屠殺,影響是看條件的

這裡我必須字斟句酌,因為我們自己曾經把話說得太滿。官方文件對於「已驗證機器人到底會不會被 Bot Fight Mode 挑戰」這件事,是保持死寂的。 我把 Bot Fight Mode 的說明頁從頭啃到尾,找不到任何一句話保證已驗證機器人能豁免,也沒看到任何一句話說他們一定會被挑戰。Super Bot Fight Mode 給了你 Verified bots 的獨立開關,那是明明白白的控制權;但免費版根本沒有對應的東西,而文件也懶得告訴你沒有的時候會發生什麼事。

既然文件不給答案,我只能告訴你我們在自家網域上親眼目睹的現實:到底會不會死,取決於「哪個開關」配上「哪一隻爬蟲」,這絕對不是一刀切的無差別封鎖。

就我們看到的狀況,即時檢索型的 ChatGPT-User (活人在對話裡觸發的抓取) 常常能全身而退,反而是專門來要訓練資料的 GPTBot 最容易在門口吃鱉。這一段我刻意不給出具體的比例,因為我們手邊沒有能公開背書的嚴謹樣本。你要看的不是我的數字,而是你自己網域上發生的真相。下一節就教你怎麼把真相挖出來。

五、動手查勤:一條 curl,一次只測一個變數

這套驗證手法很土炮,但優點就是穩:一次我們只動一個變數。

# 基準線:一般瀏覽器 UA
curl -sS -o /dev/null -w '%{http_code}\n' \
  -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/141.0 Safari/537.36" \
  https://你的網域/robots.txt

# 逐隻爬蟲試
for UA in "GPTBot/1.2" "OAI-SearchBot/1.0" "ChatGPT-User/1.0" \
          "ClaudeBot/1.0" "PerplexityBot/1.0" "CCBot/2.0"; do
  CODE=$(curl -sS -o /dev/null -w '%{http_code}' -A "$UA" https://你的網域/robots.txt)
  echo "$CODE  $UA"
done

三個判讀鐵律:

一、先測基準線。 如果連正常的瀏覽器 UA 都拿不到 200,那問題根本不在 bot 設定,是別的地方搞砸了。

二、一次只准關一個開關,關完再測。 三個開關全開的時候,你根本分不清楚是誰下的毒手。關掉 BIC 重測,如果還是 403,那兇手就是 Bot Fight Mode。這絕對比你在儀表板上瞎猜快得多。

三、把 header 也印出來看,別只盯著狀態碼。

curl -sS -D - -o /dev/null -A "GPTBot/1.2" https://你的網域/robots.txt

你要找的是這個字眼:

cf-mitigated: challenge

官方是這麼定義的:當請求撞上 Cloudflare 的 Challenge Page 而不是你原本預期的回應時,不管那是哪一種 Challenge Page,回應裡都會帶著 cf-mitigated,而且它的值必定是 challenge。這也是它唯一的有效值。

所以結論很清楚:只要看到 cf-mitigated: challenge,就代表你遇到的是挑戰,而不是無情的封鎖。 這兩件事的解法天差地別。挑戰的意思是「你算個數學證明一下你是瀏覽器」,封鎖的意思是「老子不給就是不給」。

但這裡有個我們自己踩出來的深坑:cf-mitigated 這個標頭是不保證一定會出現的。 我們親眼看過同一個網站、同樣的 403 錯誤,前一天還乖乖帶著這個 header,隔天它就消失了。所以,千萬別把它當成唯一的判斷標準。它有出現,就是鐵證;它沒出現,你也什麼都不能撇清。

六、教你從 log 揪出兇手:到底死在邊緣還是死在源站

這是實務上大家最愛問的問題,而它有一個非常乾淨俐落的解答:把三個訊號交叉比對。

訊號一:Cloudflare 的 Security Events

Security -> Analytics -> Events 分頁找。官方明講了,被 Bot Fight Mode 挑戰的請求,在 Service 欄位裡會被標記為 Bot Fight Mode (如果是 Super Bot Fight Mode 則會標 Super Bot Fight Mode)。像是 Browser Integrity Check、WAF custom rules、Managed rules、Rate limiting、IP Access rules 甚至 User Agent Blocking,全都有自己專屬的 Service 名稱。

這個欄位就是「到底誰殺了他」的直接解答。 完全不用猜。

但要注意殘酷的保留期,免費方案短得可憐:

Free / Pro Business Enterprise
24 小時 3 天 30 天

免費方案只有 24 小時的記憶,跟 Day 6 講的 AI Crawl Control 視窗一樣短命。這代表只要出事,你當天就得去查。 如果你週一才突然覺得「上週好像都沒爬蟲來耶」,週一再去查 Security Events 是絕對查不到上週的屍體的。

訊號二:源站的 access log

被邊緣保鑣擋下的請求,根本連源站的門都摸不到。所以,拿同一個時間點、同一個路徑,去源站的 Nginx 或 Apache log 裡搜搜看:

grep 'GPTBot' /var/log/nginx/access.log | tail -20
  • 如果 Security Events 有紀錄、源站 log 沒有,那就是死在邊緣
  • 如果源站 log 有紀錄,代表請求確實走到底了,後面的問題絕對跟 bot 設定無關。

但小心這裡有陷阱:源站 log 找不到,也有可能是被快取直接打發掉了 (這我們 Day 12 會花整篇文章來談)。所以這個訊號必須跟訊號一綁在一起看,絕對不能單獨定罪。

訊號三:你自己寫的 Worker 有沒有蓋上印章

如果你已經聽話照著 Day 8 部署了 Worker,那你手裡就握有第三個、也是最直接的鐵證:在回應上蓋一個你專屬的 header

const res = await fetch(outbound);
const out = new Response(res.body, res);
out.headers.set("x-crawlerlog", "ran");
return out;

之後只要看到任何一個回應,身上帶著 x-crawlerlog: ran,就鐵錚錚地證明你的 Worker 確實執行過了。這件事的價值,我們下一節見真章。

七、你的探針吃鱉,不代表爬蟲也被擋在門外

這一節是今天最沉重的一刻,因為這是我們親自搞砸過兩次的慘痛教訓。

兩次我們都犯了同一個邏輯錯誤:我們用工具去敲客戶的網站,拿到一個 403,於是大剌剌地判定「這個站在擋 AI 爬蟲」或是「我們寫的程式根本沒在跑」。兩次我們都對外發布了結論,兩次都被現實狠狠打臉。

第一次,我們對一個客戶的網站發出「你把 AI 爬蟲全擋了」的警告。結果當天就被迫撤回,因為去查那個網站的實際事件,ChatGPT-UserOAI-SearchBot 還有某個大型社群平台的爬蟲全都活蹦亂跳的,流量還不小。

第二次輸得更難看。我們鐵口直斷「我們的追蹤程式在這個站上從來沒執行過」,甚至還為此報了修。結果把回應打開一看,那個 403 錯誤的身上,清清楚楚印著我們自家 Worker 蓋上的 header。程式不僅活著在跑,那一天那個網站甚至湧入了一萬多筆事件,每一隻 AI 爬蟲都被記印得清清楚楚。

到底錯在哪?錯在我們把「自家探針遭受的待遇」當成了「爬蟲遭受的待遇」的鐵證。這兩件事根本沒有必然關係,方向甚至完全相反:挑戰層生來就是為了攔截那些來路不明、TLS 指紋毫無特色的匿名客戶端,而這正好就是 curl 的宿命。 你的 curl 被挑戰,代表這個系統正在非常正常地運作。真正的 AI 爬蟲從來不是靠 UA 混過關的,他們是靠著白名單裡的 IP 驗證大搖大擺走進來的 (這是 Day 4 的重點)。

所以,請把下面這三條當成生存法則:

  1. 先讀 header,再看狀態碼。 一個 403 錯誤,甚至一個 5xx 錯誤,只要它身上帶著你自己 Worker 蓋的印記,就證明你的程式跑過了。如果你把邏輯寫成 if (status >= 400) { 判定失敗 },那你只會重新複製我們犯過的蠢,因為你在讀 header 之前就自以為是地妄下定論了。
  2. 判斷爬蟲到底進不進得來,去查事件普查,別看你的破工具受到什麼委屈。 唯一有效的證據是「真正的爬蟲到底有沒有留下足跡」。
  3. 當探針的結果是孤立的時候,不足以下任何結論。 它必須搭配「儲存區裡確實一片死寂」來佐證。我們現在已經把這條血淚教訓寫死在程式碼的規則裡:只要近期還有流量的網站,一律封殺所有的探針告警,管你的探針在叫什麼。

八、按照你的方案,該怎麼善後

方案 想讓 AI 爬蟲進來 該怎麼解
免費 被 BIC 擋到 全域關掉,或用 custom rule 的 Skip / Configuration Rule 依路徑關掉
免費 被 Bot Fight Mode 擋到 關掉它;或者建 IP Access allow 規則搶在它之前動手;WAF Skip 絕對無效
Pro / Business 被 Super Bot Fight Mode 擋到 Verified bots 設成 Allow;或用 custom rule 的 Skip 開例外
全部 想留下審計軌跡 關掉之後跑第五節的 curl 矩陣,確實存下 before / after 的對照

忍不住要提醒一句:當你把這幾個開關關掉之後,你網域的機器人防禦網就真的變薄了。 這是殘酷的利益交換,天下沒有白吃的午餐。你想讓 AI 爬蟲大搖大擺進來,就得忍受那些你根本不想要的自動化垃圾流量也跟著混進來。我們自己的保命做法是,把「需要被 AI 爬」的網域跟「需要被嚴密保護」的資產,分開放在不同的 zone 裡面管理。千萬不要妄想在同一個 zone 上,同時追求兩件互相矛盾的事。

總結

  1. Browser Integrity Check 預設就是醒著的。 你沒碰過它,絕對不代表它沒在暗處咬人。
  2. 免費方案的 Bot Fight Mode 是一堵死牆。 它根本不走 Ruleset Engine,WAF 裡的 Skip / Bypass / Allow 對它全部像空氣一樣。網路上那些教你「寫個 skip rule 就能搞定」的教學,在免費方案上完全是誤人子弟。官方文件施捨的例外機制只有一招:用 IP Access rules 搶先匹配。
  3. 驗證請愛用 curl 矩陣,一次只動一個變數;讀 header 千萬別只看狀態碼。 cf-mitigated: challenge 出現時是鐵證,沒出現時你什麼都不能斷定。
  4. 要分辨「死在邊緣」還是「到了源站」,這題有標準答案: 去看 Security Events 裡的 Service 欄位。免費方案的記憶只有悲慘的 24 小時,當天沒查,這輩子就別想查了。

第五件事我必須單獨拉出來寫,因為它不是技術,是做這行的態度:你的探針被擋下,那只是關於你探針的悲慘事實,跟爬蟲到底進不進得來一點關係都沒有。 這句話寫出來像是一句廢話,但我們可是對外發布了兩次錯誤結論,才把這句話刻在骨頭上的。技術上的坑你花個半天就能補好,但判讀上的坑會讓你把一個完美的網站誤判成死站,然後白白浪費一整個禮拜去修一個根本不存在的問題。

明天 (Day 10) 我們來面對一個有時間壓力的未爆彈:2026-09-15 開始,Cloudflare 對於新網域的 AI 預設值要全面大洗牌了。Day 6 已經把官方公告的原文拆解過了,明天我們不講廢話。明天要給你的是既有 zone 的自我檢查清單:到底有哪些設定該去巡、怎麼確認自己有沒有被波及、cf.verifiedBotCategory 的值域到底該怎麼讀,還有,當你改完設定之後,該怎麼用 log 拿出鐵證,證明它真的生效了。

本文是「你的第一本 AEO x GEO 教戰手冊」系列第 9 天。系列實測與數據由 arrivl 整理 (Growth Analytics for the Agentic Web)。


上一篇
靠人不如靠己:不到 90 行 Cloudflare Worker,親手榨出 AI 爬蟲的真實足跡
系列文
你的第一本 AEO x GEO 教戰手冊9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言