← 返回 Siami 首頁

Cloudflare Workers Cache 正式上線:一行程式碼讓 Worker 從「每次都跑」變成「只在需要時跑」

▲ 169 💬 66
Cloudflare Workers Cache 正式上線:一行程式碼讓 Worker 從「每次都跑」變成「只在需要時跑」

編按:本文綜合整理自 Cloudflare 官方部落格、Cloudflare Workers 定價頁 與 Astro 7 發布公告,並加入 Siami 編輯部觀點與分析。

Cloudflare 正式推出 Workers Cache:一個位於 Worker 前方的分層快取(tiered cache),只要在 wrangler.jsonc 加一行 "cache": { "enabled": true },就能用既有的 Cache-Control header 控制整個快取行為。這是 Workers 平台自 2017 年推出以來,在快取架構上最大的翻新。


一行設定就啟動的分層快取

過去 9 年,Cloudflare Workers 的架構是「Worker 站在快取前面、再站在 origin 前面」。這套設計適合做 header 改寫、URL 重寫、A/B 切換等請求前處理。

但世界變了。Astro、TanStack Start、Next.js、Remix、SvelteKit 這些主流框架都內建 Cloudflare adapter,整個 app 編譯成 Worker 部署,後面沒有 origin,Worker 自己就是 origin。這種情況下舊架構沒東西可快取 — 每個請求都跑程式碼,即使回應內容與一秒前一模一樣。

Workers Cache 把架構翻轉過來,Cloudflare 的快取現在站在 Worker 前面:

{
  "name": "my-worker",
  "main": "src/index.ts",
  "compatibility_date": "2026-05-01",
  "cache": {
    "enabled": true
  }
}

之後用熟悉的 Cache-Control header 控制:

return new Response(body, {
  headers: {
    "Cache-Control": "public, max-age=300, stale-while-revalidate=3600",
    "Cache-Tag": "products,product:123",
  },
});

要清快取也直接在 Worker 裡做:

await ctx.cache.purge({ tags: ["product:123"] });

整個 API 就這樣。沒有 zone 設定、沒有 rule engine、沒有第二個產品要登入。Worker 寫在哪裡、快取就跟到哪裡 — custom domain、workers.dev、service binding、preview URL、Workers for Platforms 租戶都適用。


為什麼伺服器渲染的 app 需要前面一層快取

當 Worker 就是 origin 的時候,每個頁面載入都等於一次 render。即使 Workers runtime 已經很快(每秒處理數千萬次請求都不是問題),「每次都 render」還是有延遲成本與 CPU 時間成本。

過去要選邊站:

  • 靜態站點生成(SSG):頁面快,但每次改內容都要 rebuild。文件站幾千頁要 5-10 分鐘;大型電商網站更慘,動一個地方就要整個 rebuild。
  • 每次請求都 render:內容永遠最新,但每個訪客都要付 render 成本與延遲。

Workers Cache 提供第三條路:按需 server-render、把 render 結果快取、依照設定的 TTL 重新整理。第一次請求某個新頁面時還是會 render,但之後直到快取過期前,都像靜態頁面一樣被送出來。

這不是框架特定機制(像 Next.js 的 ISR 那些),就是照 RFC 9110 / RFC 9111 設計的標準 HTTP 快取,運作在設計來當 origin 的程式碼前面。


stale-while-revalidate 讓 SSR 站點感覺像靜態

stale-while-revalidate 是 Workers Cache 真正讓網站「感覺是靜態」的關鍵。沒有它,快取過期後第一個請求要等 Worker 從頭 render,使用者會看到延遲。有了它,過期後第一個請求立刻拿到舊版(response header 會標 Cf-Cache-Status: UPDATING),Worker 在背景重新 render。每位使用者都拿到快取速度的回應,包含觸發 refresh 的那一位。

實務上的時程模型:

  • Fresh window(max-age 內):命中快取,Worker 不跑。
  • Stale window(stale-while-revalidate 內):命中快取,Worker 在背景 refresh,使用者零等待。
  • 都過期:Worker 重新 render,使用者等待那一次 render。

商品目錄每幾分鐘更新:max-age=300, stale-while-revalidate=3600,訪客基本上不會等待。部落格文章幾乎不變:max-age=86400, stale-while-revalidate=2592000,每頁每天只跑 Worker 一次。


Vary 標頭支援:同一 URL、多種內容

實務上同一個 URL 可能要回 HTML 給瀏覽器、JSON 給 API client;同一張圖可能要 WebP 給支援的 client、JPEG 給不支援的;同一個首頁可能要英文、法文、日文。沒快取時 Worker 讀 request header 回對的就好,有快取時這通常會變醜 — 多數快取要嘛不快取這種 URL,要嘛只快取一種版本然後發給所有人。

Workers Cache 支援標準 HTTP Vary header:Worker 回 Vary: Accept-Encoding(或 Accept、Accept-Language)時,Cloudflare 對每種 header 組合分開存一份快取變體。瀏覽器送 Accept: image/webp,*/* 拿到 WebP;送 Accept: image/jpeg 拿到 JPEG。兩種都從快取來,第一次之後 Worker 完全不跑。

export default {
  async fetch(request) {
    const accept = request.headers.get("Accept") ?? "";
    const wantsWebp = accept.includes("image/webp");

    const body = wantsWebp ? await fetchWebpImage() : await fetchJpegImage();

    return new Response(body, {
      headers: {
        "Content-Type": wantsWebp ? "image/webp" : "image/jpeg",
        "Cache-Control": "public, max-age=3600",
        Vary: "Accept",
      },
    });
  },
};

沒有「允許 Vary 哪些 header」的白名單限制,照 RFC 9110 / RFC 9111 實作。


這是 Worker 的快取、不是 zone 的快取

這裡有個概念上的轉變要釐清:Cloudflare 一直都有 zone 等級的快取 — Cache Rules、Page Rules、cached-file-extensions list、Cache Reserve、Tiered Cache topology。全部都是 zone 設定,Worker 要嘛配合 zone 的設定、要嘛繞過。

Workers Cache 不一樣。它是 Worker 的快取、不是 zone 的快取,這個差別帶來幾個重要的結果:

  • 沒有 zone 設定要管理。Cache Rules、cache level 設定、Page Rules 都不套用 Workers Cache。Worker 的 Cache-Control header 就是設定。
  • 快取跟著 Worker 跑、不是跟著 hostname 跑。Worker 綁在 api.example.com、api.example.net、用 service binding 呼叫時,三個入口共用同一個快取。
  • 連 workers.dev 都有快取。Preview URL 每個有自己獨立快取(測試改動不會汙染正式環境)。Workers for Platforms 租戶的每個 user Worker 也有獨立快取。
  • 清快取範圍是 Worker entrypoint。ctx.cache.purge({ purgeEverything: true }) 只清特定 Worker,不會動到 zone 其他內容、不會影響別的 Worker。

兩層分層、每個 Worker 預設啟用

Workers Cache 預設是區域分層(regionally tiered):

  • 下層:離使用者最近的 Cloudflare 機房,每個機房有自己的下層快取。
  • 上層:橫跨整個網路的聚合層,數量較少,所有下層錯過時都會問它。

請求先打下層。命中就送出回應,沒事。沒命中下層去問上層;上層命中就回應、也順便存到下層。兩層都沒命中時 Worker 才真的跑,那次的回應會存到兩層。

這個設計重要的地方在於:世界上任何地方的第一個請求會填滿上層,之後任何機房來的請求都能從上層拿到,即使那個機房從沒見過這個請求。命中率比單層快取高非常多 — 這正是 Worker 當 origin 時最需要的。

而且這個 Tiered Cache 不用設定。沒有「幫某個 Worker 開 tiered cache」的對話框,開了 caching 的 Worker 都自動享有分層。

如果 Worker 用 Smart Placement 跟資料庫同邊執行,快取會跟它乾淨地組合:先查快取、兩層都沒命中才讓 Smart Placement 把執行路由到資料庫那邊。


🚨 為什麼這件事重要

Workers Cache 的發布對 Cloudflare 平台有三層意義,不只是「多一個快取開關」。

第一層:把 Cloudflare 從 CDN 升級成完整應用平台。 過去 9 年 Workers 一直被定位為「在 CDN 前面跑的程式」,但 Astro、Next.js 等 SSR 框架興起後,Worker 自己就是 origin 的場景越來越多。沒有快取在 Worker 前面,SSR 站點就只能在「貴、慢但即時」跟「便宜、快但要 rebuild」之間二選一。Workers Cache 把這個 false dilemma 消掉 — 對手 Vercel、Netlify 都靠 ISR 跟 edge functions 模擬類似效果,但都是框架綁定的 workaround;Cloudflare 用標準 HTTP header 做到同一件事,這是協議級的優勢。

第二層:解決多租戶 SaaS 的快取隔離痛點。 ctx.props 進 cache key 這個設計,讓認證後的 API 終於可以安全地快取 — 過去 CDN 要嘛用 user token 當 cache key(命中率慘)、要嘛每個請求都打回 origin 做授權(成本慘)。Workers Cache 讓多租戶 API 第一次之後走快取、又不會有 A 用戶看到 B 用戶資料的風險。目前市面上似乎沒有其他 CDN 內建提供這個模型,Workers for Platforms 的多租戶客戶會直接受惠。

第三層:把「近使用者」跟「近資料」兩個目標縫合起來。 Cloudflare 的網路覆蓋全球 95% 網路使用者 50ms 內,Smart Placement 讓 Worker 跑到資料庫那邊執行,但這兩塊過去很難在同一個 app 內組合。Workers Cache 透過 service binding 讓 Worker A 跑在邊緣、Worker B 跑在資料中心、A 呼叫 B 時先查 B 的快取,資料中心這一跳只在快取 miss 時付一次。這是架構上的新原語。


數據解讀:成本模型

根據 Cloudflare Workers 定價頁 與 Workers & Pages 方案:

項目FreePaid($5/月起)
月請求額度10 萬次/天1,000 萬次/月
額外請求—$0.30 / 百萬次
CPU 時間包含3,000 萬 ms/月,額外 $0.02 / 百萬 ms
Cache hit算請求、不耗 CPU同上

Workers Cache 命中只算請求、不算 CPU 時間。一個被快取命中的請求只付 $0.30 / 百萬次;render 一次可能要花數十到數百 ms CPU($0.02 / 百萬 ms)。對 SSR app 來說,每 1% 命中率提升就是直接成本下降。

實務估算:一個產品頁 50ms CPU、每秒 1000 請求、20% 命中率提升 → 每月省下約 $260 CPU 成本(簡化估算公式:$0.02 × 50ms × 1000rps × 30天 × 命中率提升)。對電商、文件站、API 服務這些高重複內容的場景,回本速度以小時計。


框架整合與下一步

Astro 7 已經內建整合 Workers Cache(Astro 7 發布公告)。Cloudflare 跟其他框架維護者合作,要把類似整合加進 TanStack Start、Next.js(透過 Vinext)。

Workers Cache 推出時所有付費方案都可用(Free plan 也有,但暫時套用 512 MB 快取大小限制,未來會依方案調整)。

Cloudflare 預告的下一步:

  • 更大回應大小限制:目前全部方案都用 Free 的 512 MB 上限,等 rollout 完成後會依方案套標準快取大小限制。
  • 框架整合擴展:TanStack Start、Next.js(Vinext)。
  • ctx.cache.invalidate() API:讓對應的快取回應表現得像過期,下一個請求還能用 stale-while-revalidate 拿到舊版、Worker 在背景重新填滿。purge 是直接刪除;invalidate 是標記過期但保留 — 給「想重新 render 但不要使用者等待」這個場景用。

上手方式

在 wrangler.jsonc 加上 "cache": { "enabled": true }、重新部署、開始設 Cache-Control header 就好。完整 Workers Cache 文件 涵蓋 quickstart、cache key、purging、composition patterns 跟除錯。

Workers 曾經只能站在快取前面。現在也能站在快取後面。看 app 需要哪一邊 — 或透過 service binding 兩邊都用。

官方公告全文:Cloudflare Blog: Your Worker can now have its own cache in front of it(Dan Lapid 與 Connor Harwood 撰寫,2026-07-06 發布)。

網友熱門留言 (5)

#1 Hacker News 開發者 ▲ 142
Workers 變成 origin 之後,舊架構根本沒東西可以快取。每次請求都跑 code,即使是純粹重複的回應也照跑,CPU 時間成本其實很可觀
#2 Hacker News 開發者 ▲ 98
一行 wrangler config 加 Cache-Control header 就能開 cache,比想像中簡單很多。Astro 7 已經原生整合了
#3 Hacker News 架構師 ▲ 87
stale-while-revalidate 真的會讓 SSR 網站感覺像靜態網站,但又犧牲 server-render 的即時性,終於不用選邊站了
#4 Hacker News 後端工程師 ▲ 64
ctx.props 自動進 cache key 這個設計太聰明了,多租戶 API 的快取隔離問題終於有乾淨解法
#5 Hacker News 評論者 ▲ 41
跟其他 CDN 比,Cloudflare 把快取放在 Worker 等級而非 zone 等級,這對 Workers for Platforms 多租戶場景差很多