如何檢測 Cloudflare 是否阻擋外部 AI Agent:從需求、誤判到自建 bot.py
當一個外部 AI 工具說「抓不到你的網站」時,直覺很容易把責任丟給 Cloudflare。尤其站上又開了 CDN、WAF、Bot 管理或其他安全機制時,第一反應通常是:是不是把外部 agent 都擋掉了?
但這次實際排查後,答案並不是那麼簡單。問題的核心不是「網站有沒有被擋」,而是要先把外部工具自己的抓取失敗,和 Cloudflare 真正的封鎖區分開來。
需求:我真正想驗證的是什麼?
這次要確認的不是一般瀏覽器能不能開站,而是更具體的一件事:
stanwu.org與blog.stanwu.org是否會對外部 AI agent 或搜尋引擎 crawler 產生差別待遇?- Cloudflare 是否對
GPTBot、ClaudeBot、PerplexityBot、Googlebot這些常見 User-Agent 做了封鎖? - 如果某個外部工具分析失敗,問題到底出在網站、Cloudflare,還是工具自己的抓取流程?
這件事和 SEO / AEO 很有關係。
因為如果主站其實沒有擋 bot,但外部工具只是偶爾抓不到,那解法就不是去亂改 Cloudflare 規則;反過來,如果 Cloudflare 真的對特定 bot 回 403 或挑戰頁,那就必須正面處理。
為什麼會有這個懷疑?
起因很單純:外部 AI 工具對同一個網站給出了前後矛盾的結論。
有些工具說主站已經是多頁品牌站,有雙語結構,也看得到 llms.txt、sitemap.xml;但另一些工具卻說很多頁抓不到,甚至把本來存在的頁面看成不存在,或把站點錯判成舊版單頁 landing page。
這種情況通常有三種可能:
- 外部工具還在看舊快取。
- 外部工具根本沒有穩定抓到 live HTML。
- Cloudflare 對某些真實 bot 或某些流量來源有限制。
如果不把這三種可能拆開,後面的判斷就很容易失真。
問題:外部工具的失敗,會被誤認成站點被擋
最大的問題在於,很多外部分析工具不會老老實實承認「我抓不到」。
有些工具抓不到頁面,仍然會根據殘缺資料、舊搜尋索引或模糊印象硬下結論;最後你看到的不是網站現況,而是它自己抓取失敗後的推測。
這樣的風險是:
- 你可能會錯怪 Cloudflare。
- 你可能會去改一堆其實沒問題的安全規則。
- 你可能會因為誤判而破壞原本正常的 CDN / WAF 設定。
所以這次的排查方向很明確:不要再依賴外部工具的二手結論,直接做一個最小可驗證的檢測工具。
解法設計:用同一支小工具模擬多種 User-Agent
最務實的做法不是上來就研究一堆 Cloudflare dashboard 設定,而是先回答一個簡單問題:
當同一個 URL 被不同 User-Agent 存取時,站到底回了什麼?
因此我做了一支很小的 Python 工具 bot.py,做的事情也很克制:
- 接受一個
--url - 用不同的 User-Agent 去請求
- 顯示
status - 顯示
final_url - 顯示幾個 Cloudflare 相關 response headers
- 從 HTML 擷取
<title> - 截一小段 body snippet
- 根據常見特徵判斷是否疑似遭到 Cloudflare challenge / block
這樣的好處是,排查焦點非常集中,不需要先引入 headless browser、代理池或複雜外掛。
bot.py 的核心思路
這支工具預設內建了幾組常見 User-Agent:
browser_chromegooglebotgptbotclaudebotperplexitybotcurl
每次測試時,它會對同一個 URL 重複做請求,然後輸出:
statusfinal_urlcloudflare_block_suspectedtitleserver,cf-ray,cf-cache-status,cf-mitigated,content-type,locationbody_snippet
Cloudflare 疑似封鎖的判斷邏輯也故意保持簡單,例如:
- HTTP status 是否是
403/429/503 - body 裡是否出現
Attention Required、challenge-platform、sorry, you have been blocked - response header 是否有
cf-mitigated
這些訊號未必完美,但已經足夠應付大多數「有沒有被擋」的第一輪判斷。
公開 gist 在這裡:
使用方式
我在本機是這樣測的:
~/bot.py --url https://blog.example.com/
如果只想測單一 agent,也可以指定:
~/bot.py --url https://blog.example.com/ --agent googlebot
~/bot.py --url https://blog.example.com/ --agent gptbot
~/bot.py --url https://blog.example.com/ --agent perplexitybot
這裡刻意用 example.com 形式展示,而不是把本機路徑、帳號或環境細節直接攤開。真正重要的是方法,不是機器上的私人上下文。
實測結果:主站與 blog 子網域都沒有被普遍擋掉
用這支工具對主站與 blog 子網域分別測試後,結果很清楚。
不論是:
browser_chromegooglebotgptbotclaudebotperplexitybotcurl
在測試當下都拿到:
status: 200- 正確的
final_url - 正常的
<title> cloudflare_block_suspected: no
而且 response headers 也能看到是經由 Cloudflare 回應,但沒有 challenge 或 block 的典型跡象。
這代表至少在當下、對這些常見 User-Agent 而言:
stanwu.org沒有被 Cloudflare 普遍封鎖。blog.stanwu.org也沒有被 Cloudflare 普遍封鎖。
這個結果真正回答了什麼?
它回答的不是「任何世界上所有 bot 都永遠不會被擋」,而是比較實際的三件事:
- 目前常見的搜尋與 AI bot UA 沒有被普遍阻擋。
- 外部工具先前抓不到頁面,不足以直接推論成 Cloudflare 封鎖。
- 如果某個分析工具仍然抓不到,很可能是它自己的抓取管線不穩,而不是站點本身拒絕它。
這個差異很重要。
因為一旦確認 Cloudflare 沒有普遍封鎖,你後續就不用沿錯誤方向一直追安全規則,反而應該把注意力放回:
- 該工具是否真的支援 live fetch
- 是否只在讀舊索引或快取
- 是否沒有完整取得 HTML head
- 是否有自己的 sandbox 限制
這次排查的關鍵經驗
1. 先做最小驗證,再做大推論
與其一開始就猜 Cloudflare、WAF、Bot Fight Mode、Rate Limiting、Robots 或 DNS,不如先做一個可以重現的最小檢測。
只要你能回答「同一個 URL 對不同 UA 到底回什麼」,很多模糊推測會立刻消失。
2. 外部 AI 工具的失敗,不等於網站失敗
這次最值得記住的一點是:
一個 AI 工具抓不到你的站,並不自動等於你的站有問題。
它可能只是:
- 沒有 live fetch 能力
- 抓取流程不穩
- 只拿到部分頁面
- 受限於自己環境的 sandbox
3. 小工具比複雜平台更適合做第一輪定位
很多時候,真正有用的不是再換一個更大的分析平台,而是一支自己能看懂、能修改、能反覆執行的小工具。
bot.py 不複雜,但它把問題切得夠乾淨:
- 這個 UA 能不能拿到 200?
- 有沒有被 redirect?
- 回來的是正常頁面還是 challenge 頁?
- 標題對不對?
這種工具越小,越不容易把調查方向帶偏。
我最後的結論
這次的重點不只是做出一支檢測 bot,而是重新確認一件事:
當系統看起來有問題時,不要急著把責任交給最顯眼的那一層。
Cloudflare 很常被當成第一嫌疑犯,這有時是對的,但有時只是因為它站在最前面。真正成熟的排查方式,是把:
- 外部工具的不穩
- 自家站點的設定
- 中間 CDN / WAF 的行為
一層一層拆開。
這樣最後得到的,不只是「有沒有被擋」的答案,而是一個之後還能重複使用的檢測方法。
如果你也在懷疑自己的站是不是被 Cloudflare 擋住某些 bot,我會建議先做一件很小但很關鍵的事:
不要先猜,先量。
而 bot.py 就是這次我用來做第一輪量測的最小工具。