Boloop 閱讀示意圖(AI 生成):檢查第一跳,才能找到驗證文件的路由問題。
Google 提供的驗證文件已經放進部署包,處理它的 Worker 單測也返回 200,但正式地址仍然不能按預期就读取。Boloop 遇到的這次問題,出在抵達處理函數之前。
實際觀察到的路徑是:请求帶 .html 後綴的驗證文件,先收到 308,跳到去掉擴展名的地址,然後返回 404。文件沒有丢失,返回它的代碼也沒有機會運行。
先看第一跳而不是最終頁面
遇到驗證失敗時,直接在瀏覽器裡打開地址有一個盲點:瀏覽器通常會跟隨跳轉。最後顯示了一個頁面,並不意味著最初那个精確地址直接返回了正確內容。
排查這類端點,我會分別檢查 GET 與 HEAD,並保留第一跳的狀態碼和 Location。GET 用來確認正文,HEAD 用來檢查沒有正文時的狀態與響應頭。它們回答的問題不同,不能只憑 HEAD 的 200 就斷言文件內容正確。
當時的 308 已經給出了一個很明確的方向:先查平台的路由與靜態資源處理,不急著修改 Google 控制台。
Pages 的便利規則與驗證地址發生了衝突
Cloudflare Pages 預設會把靜態 HTML 地址轉到不帶擴展名的形式。例如 /contact.html 會轉向 /contact。對普通頁面來說,這是平台預設的地址形式;對一個必須按指定路徑就读取的驗證端點,就需要單獨檢查。Cloudflare Serving Pages 文件
Boloop 的構建過程會生成 _routes.json,決定哪些请求進入 Worker、哪些交給靜態資源層。驗證文件當時被放進排除項,因此请求先進入靜態 HTML 處理,觸發了去擴展名跳轉。
之前的測試直接調用 Worker,繞過了這份分發清單。它證明處理函數能返回文件,卻沒有證明正式请求一定會到達處理函數。
最小修復是改變這個精確路徑的分發
這次沒有新增一套通用 HTML 回退,也沒有把所有 .html 文件都放行。修改只針對用戶提供的那一個驗證文件:保留文件原始內容,將它從 Worker 的路由排除項移出,讓既有處理函數在平台靜態規範化之前直接返回它。
文件只有 53 字節,構建檢查同時核對長度和 SHA-256。這裡的位元組與哈希是該文件的具體約束,不能複製成其他網站的驗證內容;另一個网站應使用自己從 Google 取得的原始文件。
精確例外還有一個好處:它不會順帶改變未知 HTML、後台路由或私有文件的訪問方式。一個公開驗證端點沒有理由擴大整站的靜態文件邊界。
回歸測試必須把路由分發補回来
新的測試讀取實際構建產生的 _routes.json,先判斷请求是否進入 Worker,再執行對應響應。加入舊的排除項時,測試能重現 308;去掉該精確項後,GET 與 HEAD 應直接返回 200,而且沒有 Location。
GET 還要逐位元組核對正文,HEAD 則應保持空響應體。測試同時保留普通靜態資源仍走靜態層、API 仍進入 Worker 等控制樣本,避免為了驗證文件破壞其他路徑。
這一輪有 21 項相關構建與邊界測試通過。平台分發在測試裡仍屬於模擬,部署後的精確地址还要再核驗一次。
可複用的排查順序
先確認文件確實進入最終部署產物,再请求原始地址,查看第一跳。存在跳轉時,檢查每一跳由哪一層產生;沒有跳轉但正文不對,再檢查路徑處理、編碼與返回內容。最後才回到驗證界面提交結果。
對於 Boloop,這次問題最有價值的線索不是「驗證失敗」,而是「Worker 單獨正常,經過實際分發後不正常」。以後再遇到類似差異,我會先把入口層補進測試,而不是繼續增加處理函數裡的斷言。