iT邦幫忙

2026 iThome 鐵人賽

DAY 11
0
Modern Web

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

灰雲 vs 橘雲:對 AI 爬蟲觀測的差別

  • 分享至 

  • xImage
  •  

兩個人看著同一個網站,一個說有、一個說沒有

先講一個把我整慘過的狀況。

某天下午,兩份關於同一個網域的觀測報告擺在我桌上,結論卻剛好相反。一邊說:這個站完全沒有記錄到任何流量,一筆都沒有。 另一邊則說:同一個時段每小時有 87 到 146 筆。

兩份都是實測,兩份數據都沒有寫錯。

除錯排查花了大半天,最後發現答案不在程式碼裡,不在快取設定裡,也不在防火牆規則裡,而是藏在 DNS 的一個小圖示上。這個網域的 A 記錄是橘雲,AAAA 記錄是灰雲。當初設定的人翻了 A 記錄,卻漏了 AAAA。

於是,從 IPv4 進來的訪客都被記錄了,從 IPv6 進來的訪客卻一個都沒有。而說「完全沒記錄」的那一方,測試機器剛好架在 IPv6 的環境上,發出的每個請求都走 v6,所以他看到的數據是乾淨的零。

同一個根本原因,引發了兩個完全相反的症狀。

這篇文章要談的就是那個小圖示。它是 Cloudflare 上最容易被當成裝飾的設定,但對於「你到底能觀測到什麼資料」,它是最上游的開關。這個開關的層級非常高,甚至在所有後續設定開始運作前,它就已經決定了結果。

根本差別:這個請求有沒有經過 Cloudflare

Cloudflare 的 DNS 記錄有兩種代理狀態,官方文件稱為 Proxied (介面上是橘色雲朵) 和 DNS-only (灰色雲朵)。台灣社群習慣叫橘雲跟灰雲,以下我會沿用這個說法。

先看官方文件怎麼定義,這兩句話是今天所有推論的地基:

橘雲 (Proxied):

"Cloudflare sits between your visitors and your server — optimizing, caching, and protecting traffic along the way."

而 DNS 查詢拿到的答案也不一樣:

"A DNS query to the proxied record blog.example.com will be answered with Cloudflare [anycast IP addresses]...instead of 192.0.2.1."

灰雲 (DNS-only):

"Cloudflare responds with your server's actual IP address and does not route HTTP/HTTPS traffic through its network."

把這三句話轉換成數據觀測的角度:

  • 橘雲:訪客連線的是 Cloudflare 的 IP,請求會先進 Cloudflare 的網路,跑完所有流程後,再轉交給你的伺服器。Cloudflare 能看到這個請求的完整面貌。
  • 灰雲:Cloudflare 只扮演查號台的角色,把伺服器的真實 IP 報出去,讓訪客直接連線到你的伺服器。這個請求 Cloudflare 從頭到尾都沒看過。

核心差異不在於「有沒有加速」或「有沒有防護」,而是這個請求到底存不存在於 Cloudflare 的視野裡

關於這點對觀測數據的殺傷力,官方文件針對灰雲有一句寫得特別直白:

"Cloudflare also cannot provide HTTP/HTTPS analytics on those requests (only DNS analytics)."

針對灰雲的請求,Cloudflare 甚至無法提供 HTTP 分析。不是給得比較少,是完全給不了,因為它根本沒看過這些流量。

灰雲狀態下,你以為裝好的功能都沒在跑

前面談的是運作原理,接下來要看這會造成什麼後果,而且影響範圍通常比多數人預期的還要大。

因為 Cloudflare 上幾乎所有用來觀測 AI 爬蟲的功能,都是建立在「請求必須經過 Cloudflare」這個前提上。前提不成立時,這些功能不是失效,而是根本沒有被呼叫

Worker 不會執行。 這個限制最死,官方文件也明說了:

"All domains and subdomains must have a DNS record to be proxied on Cloudflare and used to invoke a Worker."

要使用 Worker,DNS 記錄必須是橘雲。Day 8 那支自己寫的爬蟲記錄 Worker,就算程式碼寫得再好、路由 (route) 綁得再正確,只要主機名稱是灰雲,它一次都不會被觸發。而且系統不會報錯,沒有錯誤日誌,沒有警告通知,Worker 的呼叫次數就是零。這在數據上跟「網站沒人來」長得一模一樣。

WAF 規則、快取、Redirect Rules 都不會套用。 官方在橘雲的說明裡列出了這一串:

"Apply your Cloudflare product configurations (such as WAF rules, caching, and redirect rules) to incoming traffic."

反過來解讀就是:灰雲不套用這些設定。你為了擋下某隻爬蟲而寫的那條 WAF 規則,對灰雲的主機名稱來說根本不存在。

AI Crawl Control 沒有資料。 Day 6 提過它的硬性前提:「Make sure your domain is proxying traffic through Cloudflare」。今天補上那篇沒詳細說明的重點:它顯示「沒有資料」與「真的沒有流量」,在儀表板畫面上看起來完全一樣。系統不會主動提示你「有一塊流量我看不到」。

這是整個機制最坑的地方,值得特別強調:灰雲造成的觀測缺口,表現出來的症狀是「安靜」,而不是「錯誤」。 錯誤會被開發者發現,但安靜不會。你會看著一個空的儀表板,得到一個完全錯誤卻又看似合理的結論:沒有什麼 AI 爬蟲來我的網站。

IPv6 那半邊:一個網站可以同時是橘雲跟灰雲

回到開頭那個矛盾的案例。

多數人腦中的概念是「這個網域是橘雲」或「這個網域是灰雲」二選一。但這個理解是錯的,而且往往會付出很高的除錯成本。

代理狀態是綁定在「每一筆 DNS 記錄」上,而不是綁在整個網域上。 官方文件說明可代理的記錄類型如下:

"Only [records used for IP address resolution] — A, AAAA, and CNAME records — can be proxied. Other record types (such as MX or TXT) are always DNS-only."

A、AAAA、CNAME 各自都有獨立的代理雲朵設定。所以一個 example.com 完全可能處於以下狀態:

  • A 記錄 -> 橘雲 -> IPv4 的訪客會經過 Cloudflare
  • AAAA 記錄 -> 灰雲 -> IPv6 的訪客會直接連線你的伺服器

這不是理論上的極端案例 (corner case)。這正是開頭提到的那個網站的真實狀況,而且發生的原因非常平凡:在介面上這兩筆記錄是分開的兩列,各有一個雲朵圖示。你可能點了上面那列,卻漏了下面那列,而且系統完全不會提醒你漏掉了。

為什麼這點特別難查出原因: 自己去測試時,通常會覺得一切「正常」。因為走 v4 還是 v6 是作業系統當下決定的,不是你能輕易控制的。辦公室網路、家裡網路,或是手邊的筆電,通常根本沒有可用的 v6 出口。結果就是你的 curl 測試十次有十次走 v4,十次都通,然後你就得到一個結論:沒問題。

然而特定類型的流量卻不是這樣。那些架設在現代雲端機房裡、預設優先使用 v6 的自動化客戶端,會穩定地走 v6,穩定地繞過你的 Cloudflare,也穩定地不留下記錄。它們跟你走的根本不是同一條路,所以你的常規測試永遠碰不到它們遇到的狀況。

開頭那個「完全沒記錄」的觀測結果就是這樣來的。發送測試請求的那台機器處於 IPv6 環境中,測幾次就繞過幾次,得到的數字是紮實的零。而背景真實流量以 IPv4 為主,所以同時段的記錄依然有 87 到 146 筆。

這兩個觀察結果都是對的,只是各自看到了真相的一半。

實務上的結論很單純:檢查代理狀態必須一筆一筆確認,而且 AAAA 記錄要當成獨立的項目來檢查。 如果只看網站首頁通不通,或是只看 A 記錄的雲朵顏色,是絕對抓不出這個漏洞的。

建站平台的「自動綁定」會幫你建灰雲記錄

前面談的是人為設定疏漏,接下來要談另一種情況:根本不是你設定的

現在的建站與電商平台大多提供「自動綁定網域」的流程。你在平台上按一顆按鈕,它會把你導向 DNS 服務商登入,授權之後平台就直接幫你把記錄建好。這能省掉手動貼 IP 的麻煩,對一般使用者是很方便的設計。

我們實際觀測過其中一種流程的結果 (這裡使用我們自己的測試網域,平台名稱就先略過不提):它一次建了四筆記錄,包含 A、AAAA、一筆 www 的 CNAME 以及一筆驗證用的 CNAME。這四筆全部都是灰雲

從該平台的角度來看這完全合理:他們要的是流量以最短路徑進入自家基礎設施,中間能少一層是一層。平台沒有義務知道你還在 Cloudflare 上掛了其他設定。

但對網站管理員來說,結果就是:某天早上醒來,昨天還在運作的數據觀測全部歸零,而你明明什麼設定都沒動。 你唯一做的,只有「在平台上綁定網域」這件你以為跟 DNS 細部設定無關的事。

這件事有兩個後遺症值得記下來。

這往往發生在你的初始檢查之後。 任何一次性的「安裝檢查」都防不了,因為檢查通過的當下確實沒問題,平台是稍後才去動了 DNS 設定。要發現這個狀況,只能靠後續的持續複檢。

建站平台的說明文件只會教你「怎麼把網域指過來」,根本不會提代理狀態。 對平台來說那不在他們的管轄範圍內。你必須自己具備這個觀念,主動回頭去 Cloudflare 確認。

所以,只要做完以下這幾件事,務必回頭檢查一次 DNS:接上任何建站或電商平台、更換主機、新增子網域,或是申請 SSL 憑證時有人叫你「暫時關掉代理」(這個「暫時」非常容易被遺忘而變成永久)。

怎麼判斷:三個由外而內的檢查

千萬不要只從 Cloudflare 的介面來判斷。介面上顯示的只是「設定」,你需要驗證的是「實際行為」。當這兩者不一致時,往往就是你最需要找出答案的時刻。

檢查一:看回應標頭有沒有 cf-ray

這是最快的一招。cf-ray 是 Cloudflare 針對經過它的請求所蓋的專屬印章,官方定義如下:

"The Cf-Ray header (otherwise known as a Ray ID) is a hashed value that encodes information about the data center and the visitor's request."

實際跑一次:

curl -sSI https://developers.cloudflare.com/ | grep -iE '^(server|cf-ray):'

輸出 (2026-09-07 實測):

server: cloudflare
cf-ray: a370430f8cbab846-TPE

只要有 cf-ray,就代表這個請求確實經過了 Cloudflare 的邊緣節點。結尾的 TPE 是負責處理該請求的資料中心代號 (台北)。

對照組,測試一個不在 Cloudflare 後面的網站:

curl -sSI https://www.python.org/ | grep -iE '^(server|cf-ray):'
server: nginx

回應裡沒有 cf-rayserver 也不是 cloudflare。代表這個請求根本沒經過 Cloudflare。

這裡有兩個判讀上的坑,兩個都很致命:

第一,別因為 HTTP 狀態碼不是 200 就不看標頭 (Header)。 即使是一個回傳 403 的驗證挑戰頁面,只要它帶著 cf-ray,就證明請求已經成功進到 Cloudflare。這個坑我們踩過兩次:一看到 403 就急著下結論說「我們的規則沒在跑」,結果那個 403 本身正是邊緣節點程式處理完後回傳的。請務必建立先讀標頭再讀狀態碼的習慣。 如果在程式碼裡把 if (status >= 400) 寫在讀取標頭的邏輯前面,等於是親手把這個 bug 埋回去。

第二,cf-ray 只能證明「請求經過了某一個 Cloudflare 節點」,無法證明那是你的 Cloudflare 帳號在處理。 如果你的源站主機剛好也在別人的 Cloudflare 服務後面 (這在 SaaS 類型的源站很常見),你同樣會拿到 cf-ray,但那跟你的 zone 設定毫無關係。因此這招必須搭配下面的第二招一起看。

檢查二:直接問 DNS,看回傳的是誰的 IP

這一招直接對應官方對兩種代理狀態的定義:橘雲會回傳 Cloudflare 的 anycast IP,而灰雲會回傳你伺服器的真實 IP。

請用 DNS-over-HTTPS (DoH) 來查,不要用 dig 這不是工程師的潔癖,而是因為很多辦公室網路的防火牆會攔截並改寫 port 53 的 DNS 回應,連你下 dig @1.1.1.1 都一樣會被竄改。指定上游解析器根本沒用,因為攔截發生在更底層。DoH 走的是 443 加密通道,防火牆攔截不到。我們曾經為了這個現象,白白浪費了將近三小時去追查一個根本不存在的 DNS 設定問題。所以強烈建議把這條指令變成習慣:

curl -sS -H 'accept: application/dns-json' \
  "https://1.1.1.1/dns-query?name=developers.cloudflare.com&type=A"

A 記錄與 AAAA 記錄必須分開查,這是本篇的重點。 只查 A 記錄就會漏掉開頭提到的那個血淚故事:

# 兩個都要跑,不要只跑上面那個
curl -sS -H 'accept: application/dns-json' \
  "https://1.1.1.1/dns-query?name=<你的網域>&type=A"
curl -sS -H 'accept: application/dns-json' \
  "https://1.1.1.1/dns-query?name=<你的網域>&type=AAAA"

實測一個確定開啟橘雲的主機名稱,A 與 AAAA 兩邊拿到的都會是 Cloudflare 的位址:

# type=A
104.16.2.189, 104.16.3.189, 104.16.4.189, 104.16.5.189, 104.16.6.189
# type=AAAA
2606:4700::6810:2bd, 2606:4700::6810:3bd, ...(同一段)

判讀方式:如果回傳的 IP 位址落在 Cloudflare 的網段 (IPv4 常見 104.16.x172.6x.x,IPv6 是 2606:4700::/32),代表是橘雲;如果回傳的是你自己主機商的 IP,代表是灰雲。Cloudflare 官方有公開完整的 IP 位址清單可供對照。

檢查三:CNAME 鏈有沒有完整露出來

這是一個很好用的副作用,主要利用橘雲「回傳 Cloudflare IP 而不是原始目標值」的特性。

如果你查詢一筆 CNAME 記錄,結果系統把整條指向鏈原封不動地吐給你看,那就代表這筆記錄是灰雲。 橘雲會把整條鏈隱藏起來,直接回傳邊緣節點的 IP 位址。這招在追查建站平台自動綁定網域所衍生的問題時特別好用,因為它一眼就能看出「這筆 CNAME 有沒有被代理」。而 CNAME 正是這類自動連線最常遺留灰雲設定的地方。

我們在 2026 年 7 月 8 日的那次除錯,就是靠這個特徵,在幾分鐘內火速確認了哪幾筆記錄漏開代理。這比一筆一筆去翻介面快得多,而且驗證的是實際行為,不是死板的設定畫面。

翻橘雲之前:先確認 SSL 模式,不然你會把網站弄掛

抓出灰雲之後,很多人的反射動作是「那我在介面上點成橘雲就好了」。

先等一下。 這個看似簡單的動作,很有可能把一個原本活得好好的網站瞬間弄成全站 5xx 錯誤。我們確實踩過這個坑,而且還是因為「好心幫客戶把設定改正確」才引發的慘劇。

翻橘雲之後 Cloudflare 會變成中間人,它用哪個協定去跟你的源站 (Origin) 溝通,是由 SSL/TLS 模式決定的。官方定義如下:

Flexible: "Traffic from visitors to Cloudflare can be encrypted via HTTPS, but traffic from Cloudflare to the origin server is not."
Full: "Cloudflare matches the visitor request protocol when connecting to the origin. If the visitor uses HTTP, Cloudflare connects to the origin via HTTP; if HTTPS, Cloudflare uses HTTPS without validating the origin's certificate."

這會引發兩種完全相反的災難,各自對應不同的源站類型:

災難一:源站只支援 HTTP,你卻在 Cloudflare 設了 Full 模式。 Cloudflare 會嘗試用 HTTPS 去敲一個沒開 443 port、沒有憑證的源站,導致握手失敗。官方對 525 錯誤的定義:

"This error indicates that the SSL handshake between Cloudflare and the origin web server failed."

結果就是全站 5xx 錯誤。這類源站常見的樣貌,通常是只服務 HTTP 的老舊自架網站。它在灰雲時代活得非常安好,因為根本沒有人在中間試圖用 HTTPS 去連線。

災難二:源站強制把 HTTP 重新導向至 HTTPS,你卻設了 Flexible 模式。 Cloudflare 用 HTTP 去請求,源站回覆一個「請改用 HTTPS」的重新導向 (Redirect)。這個導向回到 Cloudflare 後,Cloudflare 又繼續用 HTTP 去請求,於是形成無窮迴圈的轉址地獄 (Infinite Redirect Loop),最後瀏覽器會直接跳錯。

這裡要釐清一個反直覺但非常重要的觀念:Flexible 搭配橘雲並不是錯誤的組合。「讓 Cloudflare 擋在一個只支援 HTTP 的源站前面」,這本來就是這套產品的典型應用場景。無窮迴圈的罪魁禍首不是 Flexible 模式,而是源站端的強制重新導向。一個設定正常的源站,應該要信任 Cloudflare 傳遞過去的協定標頭 (Header),並乖乖回傳 200 狀態碼。

所以,網路上常說的「看到 Flexible 就一律改成 Full」是完全錯誤的解法,而且是會直接導致網站掛掉的嚴重錯誤。這樣做等於是把對災難二的擔憂,直接轉換成災難一的實際停機事故。我們曾經寫過的一版腳本也是無條件把 Flexible 改成 Full,後來痛定思痛改成:先探測源站到底能不能接受 HTTPS 連線。可以的話才改 Full;如果不行的話,就保持 Flexible 然後直接開橘雲。

翻橘雲前的最小檢查清單:

  1. 在還是灰雲的時候,直接在瀏覽器打 https://<你的網域>。這時候打到的是源站本人。有通 = 源站支援 HTTPS = Full 模式可以用。不通 = 只支援 HTTP = 保持 Flexible 模式。
  2. 用 HTTP 打一次,看它會不會硬轉去 HTTPS。會硬轉、卻又只講 HTTP 的,是唯一真正需要額外處理的麻煩組合。
  3. 翻完橘雲之後,立刻用 v4 跟 v6 各發送一次請求,確認兩邊都拿得到 cf-ray,而且都沒有出現 5xx 錯誤。只改對一半的下場,就是本文開頭提到的那個血淚故事。

如果要把 Cloudflare 擋在 SaaS 建站平台前面,憑證與 SSL 還有另一層更深的地雷要踩。那是 Day 14 會探討的主題,今天先點到為止。今天你只需要牢記一個核心觀念:開啟橘雲是一個會徹底改變流量連線路徑的實體動作,絕對不是單純切換顯示圖示而已。

小結

  • 橘雲跟灰雲的差異重點不在於「有沒有加速」,而是這個請求到底有沒有進入 Cloudflare 的視野裡。針對灰雲的請求,Cloudflare 官方明文規定無法提供任何 HTTP 分析數據。
  • 在灰雲狀態下,Worker 不會被觸發、WAF 規則不會套用、AI Crawl Control 也會毫無資料。最可怕的是,症狀表現為徹底的安靜,而不是報錯。儀表板空無一物,跟「真的沒有流量」的畫面一模一樣。
  • 代理狀態是綁定在每一筆 DNS 記錄上,而不是綁在整個網域上。 如果 A 記錄開了橘雲、AAAA 卻漏了沒開,結果就是 IPv6 的訪客會全體隱形,而且用一般的測試方法多半根本抓不出問題。
  • 建站平台提供的「自動綁定網域」功能,很可能會默默幫你建出一整組灰雲記錄,而且往往是在你做完初始檢查之後才動手修改。只要綁定了這類平台,務必主動回頭去 Cloudflare 檢查 DNS 狀態。
  • 驗證步驟要由外而內:檢查標頭的 cf-ray 確認流量有無經過、用 DoH 分別查 A 與 AAAA 記錄看回傳的是誰的 IP、觀察 CNAME 鏈有沒有完整露出來。務必先讀標頭再看 HTTP 狀態碼,收到 403 並不代表你的防護規則沒有在運作。
  • 開橘雲之前,務必先確認源站是否支援 HTTPS 連線。網路上那種「看到 Flexible 就一律改 Full」的建議,是會直接造成網站停機的錯誤解法。

今天談的是「請求根本沒進 Cloudflare」這種嚴重盲區。明天要來談另一種更隱蔽的狀況:請求順利進來了,Cloudflare 也看到了,但你的伺服器卻永遠不知道有這件事。因為快取命中的請求不會回到源站 (Origin),所以源站的存取日誌 (Access Log) 跟埋在應用程式裡的前端追蹤事件,對這些請求一律是空白。Day 12,我們就來實際測量看看那塊空白到底有多大。


本文是「你的第一本 AEO x GEO 教戰手冊」系列第 11 天。系列實測與數據由 arrivl 整理,這是一個專注於 Growth Analytics for the Agentic Web 的工具。


上一篇
Cloudflare 新制下既有 zone 的 AI 自查清單
系列文
你的第一本 AEO x GEO 教戰手冊11
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言