今天打開 Search Console,看到這一行:
1 | /sitemap.xml 2026年6月8日 2026年6月11日 成功 612 |
狀態「成功」,系統探索到的網頁 612。我盯著它看了幾秒,因為文章也累積到一些數量了,想著要讓Google搜尋能搜尋到,結果到現在,這一欄一直是紅色的「無法擷取」。整整三個月,Google 連我的 sitemap 都不願意讀,全站文章只有首頁被索引。
最後解決問題的那一步,跟 sitemap 的內容一點關係都沒有。這篇把整個排查過程寫下來,包含三次「修對了東西但沒解決問題」的彎路——如果你的 GitHub Pages 部落格也卡在這個狀態,也許可以少走幾步。
問題長什麼樣
我的部落格是 Hexo 生成、部署在 GitHub Pages(kyosora.github.io)。三月中建站時把 sitemap.xml 提交到 Search Console,狀態顯示「無法擷取」(Couldn't fetch)。
當時想說剛建站,Google 需要時間。結果這個狀態凍結了快三個月,期間「已送出」日期一直停在 3 月 17 日,系統探索到的網頁是 0。
詭異的是,sitemap 檔案本身完全正常。瀏覽器打得開,curl 模擬 Googlebot 的 UA 去抓也是 200:
1 | curl -sSIL -A "Mozilla/5.0 (compatible; Googlebot/2.1; ...)" https://kyosora.github.io/sitemap.xml |
檔案抓得到、格式正確、大小 85KB 遠低於 50MB 上限。但 GSC 就是說它無法擷取。
第一輪:robots.txt 真的有問題
五月底我跟 Claude Code 一起把整個站翻了一遍,第一個有實質意義的發現來自 GSC 的「測試線上網址」功能——資源載入清單裡有一排紅字:
1 | Googlebot 遭到 robots.txt 封鎖 |
我建站時抄來的 robots.txt 範本封鎖了 /css/ 和 /js/。本意大概是「不想讓爬蟲浪費時間在靜態資源上」,但對現在的 Google 來說這是反效果:Googlebot 要渲染頁面才能評估品質,你把 CSS/JS 全擋掉,它看到的就是一個破版的骨架。
把 robots.txt 改成全開,順手把 sitemap 申報加上去:
1 | User-agent: * |
改完重新提交 sitemap。幾天後狀態真的變了——從「無法擷取」變成「無法讀取 Sitemap」。
聽起來像進度,實際上是換了一種失敗。「無法擷取」是 HTTP 層拿不到檔案,「無法讀取」是拿到了但解析不了。Google 終於肯下載我的 sitemap,然後告訴我內容有問題。
第二輪:sitemap 裡真的有重複 URL
既然 Google 說讀不了,那就把檔案拆開來驗。用 PowerShell 把所有 <loc> 抓出來分組,找到 6 個重複的 URL。腳本是 Claude Code 現場拼的,核心就這幾行:
1 | $urls = [regex]::Matches((Get-Content sitemap.xml -Raw), '<loc>([^<]+)</loc>') | |
重複有兩種來源,第二種我覺得值得單獨講。
第一種:兩篇文章生成了同一個網址。我用 hexo-abbrlink 做永久連結,它拿文章內容算 CRC32。翻原始檔才發現 _posts 裡躺著一篇某次操作失誤產生的複製檔——內容跟另一篇一模一樣,只有標題欄位不同。內容相同,CRC32 就相同,兩篇文章指向同一個 URL。刪掉重複檔就解了。
第二種:tag 命名不一致。這個坑藏得深。Hexo 把 tag 名稱轉成網址時會做正規化——空格變連字號、大小寫合併。所以這些寫法:
| 文章 A 的 tag | 文章 B 的 tag | 生成的網址 |
|---|---|---|
AI Agent |
AI-Agent |
都是 /tags/AI-Agent/ |
Hexo |
HEXO |
都是 /tags/Hexo/ |
Prompt Engineering |
prompt engineering |
都是 /tags/Prompt-Engineering/ |
在 Hexo 內部它們是不同的 tag 物件,各自產生一個 tag 頁,但網址撞在一起——sitemap 就老老實實輸出了兩次。我的站累積了 5 組這種重複。Google 官方文件沒明說重複 <loc> 會怎麼處理,但至少在我的案例裡,重複沒清掉之前,它就是一直停在「無法讀取」。
把少數派的 tag 寫法改成跟多數一致,hexo clean 重建,驗證 667 個 URL 零重複,部署,重新提交。
這次我很有信心。然後等了兩天,還是「無法讀取」。
第三輪:檔案已經完美了,所以問題不在檔案
到這裡我開始懷疑人生,把所有能想到的檢查全跑了一遍:
- gzip 壓縮版驗證——GitHub Pages 會對 Googlebot 回傳 gzip,所以特地帶
Accept-Encoding: gzip抓下來解壓驗證,正常 - BOM 檢查——無
- XML 控制字元——無
lastmod日期——格式合法、沒有未來日期- 重複 URL——零
每一項都是綠的。一份在技術上挑不出任何毛病的 sitemap,Google 就是讀不了。
(這期間還試了兩條死路:Google 的 sitemap ping API,2023 年就廢棄了,打過去直接 404;IndexNow 只有 Bing 系在收,Google 不認。)
接著我看到有人分享:sitemap 換個檔名重新提交就好了。邏輯上說得通——GSC 的失敗狀態是綁在 URL 上快取的,sitemap.xml 這個網址被標記失敗後,重新提交同一個網址可能清不掉舊狀態;換個全新的檔名,Google 沒有它的歷史包袱,會當成新東西從頭處理。
hexo-generator-sitemap 的 path 原生支援陣列,改一下設定就能同時輸出兩份:
1 | sitemap: |
提交 sitemapV2.xml,再等。五天,還是「無法擷取」。
轉折:問題不在檔案,在路徑
等待的這幾天,GSC 的「檢索統計資料」報告把另一個事實攤在我面前:90 天裡 Google 總共只來爬了 83 次,平均一天不到一次,其中 36% 還撞在 404 上;全站被索引的頁面只有首頁 1 頁。Google 對 kyosora.github.io 這個 host 的態度,用冷淡形容都太客氣。
6 月 8 日,我找到 Ronnie Wong 的一篇文章,他在 GitHub Pages 上遇到一模一樣的狀況:sitemap 怎麼換檔名、換格式(XML、TXT、巢狀、sitemap index)都卡「無法擷取」,連用 API 查都只有 isPending: true,沒有錯誤也沒有下載紀錄。
他最後有效的做法,是把 sitemap 搬到 Cloudflare Worker 上,用 workers.dev 的網址另開一個 property 提交。幾天後,成功。
他的結論我直接引用,因為這就是整件事的核心:
當多個 sitemap 檔名和格式都失敗時,不要繼續只改 XML;要換 host、換 property、換提交路徑。
我前面三輪都在同一個假設裡打轉:「問題出在 sitemap 檔案本身」。但所有證據其實早就指向另一邊——檔案每一項檢查都過,Google 就是不下載。那有問題的不是檔案,是 Google 通往 kyosora.github.io 這個 host 的讀取路徑。具體卡在哪一層(CDN、GitHub Pages 的某種行為、GSC 內部對 github.io 的處理)我到現在也不知道,GSC 是黑箱,它不會告訴你。但既然路不通,那就換條路。
實作:30 行的 Worker
做法比想像中簡單。Worker 不用存任何內容,它就是個轉發器——Google 來要 sitemap,它即時去 GitHub Pages 抓最新版回給 Google:
1 | const ORIGIN = 'https://kyosora.github.io'; |
(cf.cacheTtl 是 Cloudflare 給 Workers fetch 的專有選項。一開始我沒設,結果改完原站 sitemap 後 Worker 一直吐快取的舊版,加上這行重新部署才即時更新——這是實際跑過驗證的版本,不是查文件抄的。)
wrangler deploy 推上去,拿到一個 *.workers.dev 網址。然後在 GSC:
- 新增資源 → URL 前置字元 → 填 Worker 網址
- 驗證方式選「HTML 標記」(meta tag 已經寫在 Worker 首頁回應裡)
- 進新 property 提交
sitemap.xml
因為 Worker 是即時轉發,以後我 hexo deploy 完全不用管它——Google 來要的永遠是最新版。維護成本是零。
提交當下狀態一樣顯示「無法擷取」,這是 GSC 的佔位狀態,「上次讀取時間」空白就代表 Google 還沒真的來抓,不用慌。
三天後,6 月 11 日:成功,612 頁。
兩件事先說清楚,免得你跟我一樣空歡喜或搞混:
第一,「系統探索到的網頁 612」是探索(discovered),不是索引。意思是 Google 終於拿到了完整的 URL 清單,知道這 612 頁存在——但要不要爬、要不要收進索引,它還是按自己的步調排隊。入口打通了,後面還是要等。
第二,property 不會搞混。Worker 那個 workers.dev property 只有一個用途:當 sitemap 的提交與監控入口。sitemap 裡列的全是 kyosora.github.io 的網址,Google 爬完索引完,成效、曝光、索引數據統統記在原本 kyosora.github.io 的 property 底下。日常分析照舊看原 property,Worker property 一年大概只需要點開一次確認 sitemap 還是綠的。
回頭看這三個月
把四輪修復攤開看:
- robots.txt 解除 CSS/JS 封鎖——該修,修完曝光確實開始爬升,但跟 sitemap 無關
- 清掉 6 個重複 URL——該修,髒的 sitemap 給誰都讀不了,但修完還是讀不了
- 換檔名——白做,因為還在同一個 host 上
- 換 host——三天解決
前兩輪不算白費,它們把「檔案有問題」這個變數徹底排除了,第四輪的判斷才站得住腳。真正浪費時間的是第三輪——我在同一個假設裡多繞了五天,因為「換檔名」感覺像是在改變什麼,實際上關鍵變數一個都沒動。
對了,第二輪那個 tag 命名的坑值得長記性:寫新文章加 tag 前,先查一下站內既有的寫法。我在修復期間發了篇新文章,tag 寫了 vibe coding,而站內已經有篇文章用 Vibe Coding——又一組重複就這樣誕生,被部署前的驗證腳本攔下來。這種錯誤你犯第二次的時候,連自己都想笑。
最後說個直白的感想。下次再遇到黑箱系統給模糊錯誤,我會早一點問自己:我是在修「它說有問題的東西」,還是在修「我修得動的東西」?這兩個常常不是同一個。





