工程 · 2026.10.25 · 4 min · 更新於 2026.10.26
發文要等兩分鐘:把 Astro 靜態部落格改成 Workers 即時渲染
部落格原本每次發文都要全站重 build,等 1 到 2 分鐘。改成 Cloudflare Workers 即時渲染加邊緣快取後,發文只清一次快取,約 5 秒上線,CPU 中位數 9ms,還留在免費方案。
閱讀設定
按下發佈之後,這個 Astro 靜態部落格要全站重 build,1 到 2 分鐘後讀者才看得到新文章。
我把它改成在 Cloudflare Workers 上即時渲染、結果存進邊緣快取。發文時後端只要清一次快取,大約 5 秒就上線。
線上量到的渲染 CPU 中位數 9ms,命中快取只要 1 到 3ms,目前還在免費方案。
過程中最容易漏的坑是:Worker 存進 Cache API 的回應,會被 zone 的瀏覽器快取 TTL 改寫。
後端照舊當 CMS
前台是 Astro,放在 Cloudflare Pages;文章存在自架的 Spring Boot 加 PostgreSQL 後端,從自己的後台發佈。
發文時後端觸發 GitHub Actions 全站重 build,文章越多越慢。我想要的是像 WordPress 那樣,按下發佈就上線。
最後選的是讓 Worker 即時渲染頁面,後端繼續當 CMS。
另外兩條路都要重寫一大塊。讓後端直接產 HTML,等於整個前台重寫;把資料全部搬到 D1 和 R2(Cloudflare 的資料庫和檔案儲存),登入、2FA、留言、推播都要重寫。
用 KV(Cloudflare 的鍵值儲存)存渲染結果也想過,但 KV 全球同步要一分鐘左右,發文沒辦法馬上生效,所以排到之後。

先用 POC 量 CPU 和網路
切換前先在 workers.dev 和一個臨時子網域上做 POC,量完就刪。
Cache API 在 workers.dev 網域上不會生效,put 等於沒做,測快取得掛自己的網域。
300 多次渲染沒有失敗,渲染一頁的 CPU 時間如下:
| 情況 | 渲染 CPU 時間 |
|---|---|
| 命中快取 | 1 到 3ms |
| 本機熱身後 | 約 3ms |
| 線上中位數 | 9ms |
| 線上 p90 | 10ms |
| 線上範圍 | 4 到 46ms |
意外的是網路。從台灣連 Cloudflare 會被繞到 SJC(聖荷西),Worker 每打一次後端約 350ms。
所以文章頁的清單和內文改成同時抓,不要一個等一個。
快取 key 與 Cache-Tag
Astro 加上 Cloudflare adapter,首頁、文章、tag、封存、RSS、sitemap、404 改成在 Worker 即時渲染,關於頁和後台維持靜態。
渲染結果由 middleware 存進 Cache API,有三個細節:
- 快取 key 帶 build ID,部署新版就自動換一批快取,舊 HTML 不會指到已經刪掉的 CSS。
- key 去掉 query string,亂帶參數打不穿快取。
- 存進去的回應加上
Cache-Tag: html,lang:<語系>,讓後端用 tag 清。
Cache API 是每個資料中心各自一份,清快取則是全球生效。
用網址或用 tag 清快取,對 Cache API 都有效,只清到指定的頁面。免費方案也能用 tag 清。
後端在發佈、更新、刪除已發佈的文章,或排程文章到點時,清掉 tag html。它會等最後一次觸發後 3 秒才送,連續操作合併成一次。
被改寫的 Cache-Control
Worker 用 caches.default.put() 存進去的回應,之後 match() 拿出來時,Cache-Control 已經不是當初存的值。
zone 的瀏覽器快取 TTL 預設 4 小時,會把它改成 max-age=14400。
直接回給讀者的話,瀏覽器會留著舊頁 4 小時,後端清了邊緣快取也沒用。
改法是把邊緣和瀏覽器分開管。存進快取的那份用 s-maxage(只給 CDN 這類共用快取看的存活時間)控制邊緣存多久。
不管命中或沒命中,送出前都把給瀏覽器的 header 蓋回去,讓瀏覽器每次回來問邊緣。存的那份用 clone(),兩邊互不影響。
res.headers.set('Cache-Control', 'public, s-maxage=604800') // 給邊緣,數值是例子
await caches.default.put(key, res.clone())
res.headers.set('Cache-Control', 'public, max-age=0, must-revalidate') // 給瀏覽器

切換結果與還沒解的問題
切換時 Worker 用路由攔在 Pages 前面,Pages 留著當退路,刪掉路由就退回原站。
正式站實測清快取,狀態照預期從 HIT 變 MISS、再回到 HIT。
有幾個舊問題跟著解掉。
之前刪掉的兩篇文章,在 Cloudflare 清了兩次快取都還在。查下去是 Pages 原站自己回舊版本,帶 noindex、快取一週,清 zone 快取碰不到;切到 Worker 之後直接 404。
RSS 原本讀 repo 裡的舊 markdown,訂閱的人看不到新文章,這次改讀後台。
搜尋原本用 Pagefind,索引要 build 時產生,改成打後端的搜尋 API。
封面改走 Cloudflare 的圖片轉換,從同網域輸出,首頁 WebGL 貼圖不會有 CORS 問題。
Worker 裡推算遠端圖片寬高會失敗,所以改成後端上傳時讀檔頭記下寬高,舊的 252 張圖補算,74 篇內文重新 render。
還沒解的:清快取在最後一次觸發後 3 秒才送,這 3 秒內後端剛好重啟,就會漏清一次。
CPU 也還在觀察,常超標就先加 KV 存渲染結果,再不行才升級付費方案。
這次最大的收穫是,靜態站發文慢不一定要換掉後端,讓邊緣即時渲染、後端照舊當 CMS,改動範圍最小。

投資
回測結果可信嗎?TradingView 回撤低估、偷看未來等五個回測陷阱
回測跑出漂亮數字,不代表策略真的有用。整理我做策略研究時踩過的坑:TradingView 最大回撤低估、沒有隨機對照組、資料沒洗、樣本太短、偷看未來,以及各自怎麼避開。
留言
載入留言中…