GitHub 已經 push,Vercel 正式站卻還是舊內容:部署 freshness 排查筆記

內容站最容易讓人誤判的狀況之一,是本機和 GitHub 都看起來正常,但正式網域還在服務舊頁面。尤其是每日新增文章時,git push 成功不等於讀者已經看到新文章;Vercel 顯示有 deployment,也不等於自訂網域已經切到最新 HTML。
UCAMC 的做法是先把問題拆開:內容是否真的進入 Git、Vercel 是否真的用那個 commit 部署、root-level 文章路由是否在 production 回 200、/blog 與 sitemap 是否都包含新 slug。這幾件事要分層驗證,才不會把部署 freshness 問題誤修成內容或 SEO 問題。
先確認 Git 內容,而不是先猜 Vercel 壞掉
第一步是確認目前工作區沒有漏掉的 Markdown 或版面修改。每日內容站維護常見失誤,是新文章存在於本機 content/blog,但沒有被 commit;或 commit 已存在,但 push 沒有成功送到 origin/main。
可以先檢查:
git status --short
git log -1 --oneline
git ls-remote origin main
如果 git status --short 還有新文章未追蹤,正式站當然不會更新。若 git log -1 和 git ls-remote origin main 指向不同 commit,問題也還在發布到 GitHub 之前,不需要急著調整 Vercel 設定。
再確認本機 production 產物是否真的有新路由
對 Next.js App Router 內容站來說,開發模式能看到文章,不代表 production build 一定會把 route 產出。UCAMC 文章 canonical URL 是 root-level /{slug},所以要直接測新文章根路徑,而不是測 /blog/{slug}。
npm run lint
npm run build
npm run start -- --hostname 127.0.0.1 --port 3100
接著用 curl 檢查幾個入口:
curl -I http://127.0.0.1:3100/
curl -I http://127.0.0.1:3100/blog
curl -I http://127.0.0.1:3100/vercel-github-deployment-freshness-troubleshooting
curl -I http://127.0.0.1:3100/sitemap.xml
如果本機 production 的新文章就是 404,問題多半在 frontmatter、slug、content loader 或 build 輸出。若本機全部正常,才進入部署 freshness 層。
分辨「文章 route 404」和「列表還沒更新」
正式站 stale 可能有不同型態:
| 現象 | 可能原因 | 下一步 |
|---|---|---|
新文章 /{slug} 404 | Vercel 還沒部署新 commit,或 deployment 失敗 | 查 deployment commit / build log |
新文章 200,但 /blog 沒有新卡片 | 列表頁快取或不同 deployment | 比對 HTML、sitemap、deployment URL |
/blog 有新 slug,但 sitemap 沒有 | sitemap 產生邏輯或 stale response | 檢查 app/sitemap.ts 與 response |
| preview domain 新、自訂網域舊 | custom domain / edge cache freshness | 分開 poll 兩個 domain |
排查時不要只看首頁截圖。首頁可能因為 Hero 只顯示最新一篇,讓人誤以為內容已更新;也可能首頁還在舊快取,但文章 route 已經能開。要把 /、/blog、/{slug}、/sitemap.xml 分開記錄。
自訂網域和 Vercel preview domain 要分開看
UCAMC 這類內容站通常同時有 Vercel preview domain 和正式自訂網域。兩者若回應不同,不要只說「production 壞了」,而要寫清楚哪一個 domain stale。
建議記錄格式:
ucamc-next-blog.vercel.app /{slug}: 200, contains slug
www.ucamc.com /{slug}: 404, stale body
ucamc-next-blog.vercel.app /blog: 200, contains title
www.ucamc.com /blog: 200, missing title
這樣回報時,Leon 或有 Vercel 權限的人可以直接判斷是否需要 dashboard redeploy、deploy hook、domain 設定檢查,或只是等 edge cache 更新。
SEO freshness:sitemap 和 canonical 也要一起看
內容站不只要讓讀者能打開文章,也要讓搜尋引擎看到一致 URL。新文章上線後,我會確認:
- 文章 HTML canonical 指向
https://www.ucamc.com/{slug}或 root-level canonical path。 /sitemap.xml包含https://www.ucamc.com/{slug}。/robots.txt的Sitemap:指向正式 domain 的 sitemap。- 內部連結使用
/{slug},不是新建/blog/{slug}。
這裡的重點不是 keyword stuffing,而是避免搜尋引擎看到互相矛盾的 canonical、舊列表與新文章 route。
把 freshness 問題寫進維護紀錄
如果本機 lint/build/route 都正常,GitHub 也已經是最新 commit,但正式 domain 仍舊,這應該被報告成「部署 freshness 邊界」,不是把內容再改一次。重複修改 Markdown 只會產生更多 commit,卻不一定解決 Vercel 或 domain cache 問題。
一份好的紀錄至少包含:
- 新 commit hash。
- 本機 production route 檢查結果。
- Vercel preview domain 檢查結果。
- 正式 domain 檢查結果。
/blog與 sitemap 是否含新 slug。- 是否需要 Vercel dashboard redeploy 或 token/deploy hook。
UCAMC 的日常判斷原則
如果你正在維護 Next.js 內容站,可以把這篇接在 Next.js 內容站上線後健康檢查模板 後使用。前者幫你確認 route 和 sitemap 是否合理;這篇則處理「GitHub 已更新,但正式站看起來沒更新」的中間地帶。
對長期經營的技術 Blog 來說,穩定發布比單次快速改版更重要。把 Git、build、deployment、edge cache 和 SEO freshness 分層記錄,下一次遇到正式站 stale,就能用證據判斷,而不是靠猜測重做內容。