快取是怎麼運作的(命中、未命中、重新驗證)
看清楚邊緣節點何時秒回、何時回源、何時把過期的複本刷新。
我們要看懂什麼?
每當瀏覽器向 Cloudflare 要一個檔案,邊緣節點(edge node,離訪客最近的快取伺服器)都會跑同一個快速判斷:我手上有沒有還新鮮的複本?有的話,它在幾毫秒內就回覆(快取命中 HIT)。沒有的話,它就去你的來源伺服器(origin,你真正放網站的那台主機)或 Worker 抓取,視情況把回應存起來,再送出(快取未命中 MISS)。這篇用圖把整個生命週期畫出來,讓你一眼看懂。
把它想成一台冰箱
邊緣快取就是你家冰箱,來源伺服器則是超市。牛奶在冰箱裡又還沒過期(TTL)就直接喝(HIT);冰箱裡沒有就跑一趟超市(MISS);剛好過期一點點,就先聞一下確認再決定要不要買新的(重新驗證 revalidate)。
圖上這些名詞的意思
在看流程圖之前,先認識五個一直出現的名詞。每一個都對應到下面圖裡的某個方框或箭頭。
邊緣快取(edge cache)
Cloudflare 離訪客很近的伺服器,存著你回應的複本,能直接回覆而不用打擾你的來源伺服器。
來源伺服器(origin)
你真正產生回應的伺服器或 Worker。只有 MISS 或要重新驗證時,邊緣節點才會回到這裡。
TTL(存活時間)
Time To Live,快取複本被當成新鮮的秒數。由 Cache-Control 的 max-age / s-maxage 或快取規則(Cache Rule)決定。
新鮮 vs 過期(fresh / stale)
在 TTL 內的複本是新鮮的,可立即送出;超過 TTL 就變過期(stale),必須重新驗證或重新抓取。
清除 / 失效(purge)
在 TTL 還沒到之前就手動把快取複本踢掉——可依網址、標籤或全部清除——讓下一個請求重新建立。
快取鍵(cache key)
邊緣節點用來查複本的依據,預設就是完整網址。兩個不同網址就是兩筆不同的快取。
命中 vs 未命中 vs 重新驗證
同一個網址,會因為邊緣節點手上有什麼而表現不同。這張時序圖示範同一個 /logo.png 被請求三次:第一次是冷的(MISS)、接著是熱的(HIT)、最後是 TTL 過期之後(重新驗證)。
重新驗證很省頻寬
重新驗證時,邊緣節點用驗證碼(ETag / Last-Modified)去問「這個有沒有變?」。回 304 Not Modified 代表它可以繼續用手上那份複本,只要把計時器刷新就好,不必整份重新下載。
TTL 隨時間變化:新鮮、過期、重新驗證
每份快取複本都附帶一個計時器。從被儲存的那一刻起,它在 max-age 用完前都算 FRESH(新鮮),之後變成 STALE(過期)。過期後的下一個請求才會觸發重新驗證,而驗證結果會把計時器重設回 FRESH。
stale-while-revalidate(過期照送、背景刷新)
用 Cache-Control: max-age=60, stale-while-revalidate=600 時,邊緣節點可以先把過期的複本立刻送給使用者,同時在背景回源刷新。訪客完全不用等重新驗證。
看懂 cf-cache-status
Cloudflare 會用一個叫 cf-cache-status 的回應標頭(response header),告訴你這個請求走了流程圖的哪一條分支。下面是你最常遇到的幾個值。
HIT(命中)
邊緣快取裡有新鮮的複本,立刻送出。完全不用回源,是最快也最省的結果。
MISS(未命中)
這個鍵沒有快取,所以邊緣節點回源抓取,若允許就存起來給下次用。
EXPIRED(過期)
原本有複本,但 TTL 到了,所以邊緣節點重新驗證或回源抓一份新的。
DYNAMIC(動態)
內容被視為動態、預設不適合快取,所以每個請求都回源。
BYPASS(略過)
刻意略過快取——可能是某條快取規則、no-store 標頭,或設定要求邊緣節點不要快取。
REVALIDATED(已重新驗證)
過期複本拿去跟來源核對(ETag / If-None-Match),來源回 304,於是同一份複本被重複使用。
動手控制:設標頭 + 驗證
上面這一切,都是用來源或 Worker 送出的一個 HTTP 標頭來操控:Cache-Control。max-age 是瀏覽器 TTL;s-maxage 是邊緣(共用快取)TTL,在 Cloudflare 會蓋過 max-age。
# Static asset: 1 hour in browsers, 1 day on the edge
Cache-Control: public, max-age=3600, s-maxage=86400
# Serve stale instantly, refresh in the background for 10 min
Cache-Control: public, max-age=60, stale-while-revalidate=600
# Per-user / logged-in page: never cache anywhere
Cache-Control: private, no-storeexport default {
async fetch(request) {
const body = JSON.stringify({ hello: "world" });
return new Response(body, {
headers: {
"Content-Type": "application/json",
// Browser keeps it 1h; Cloudflare edge keeps it 1 day
"Cache-Control": "public, max-age=3600, s-maxage=86400"
}
});
}
};送出標頭
部署你的 Worker,或設定來源伺服器,讓可快取的回應帶上 public 的 Cache-Control 並設定 s-maxage。
連續請求兩次
用 curl -sI 跑兩次。第一次通常是 MISS(冷的),第二次從同一個邊緣城市應該變成 HIT(熱的)。
# -s silent, -I headers only; look at cf-cache-status curl -sI https://example.com/logo.png | grep -i cf-cache-status # 1st run -> cf-cache-status: MISS # 2nd run -> cf-cache-status: HIT更新後清除快取
在 TTL 還沒到就改了檔案?清除它,讓下一個請求重建一份新的複本。
# Purge a single URL via the API curl -X POST \ "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \ -H "Authorization: Bearer $CF_API_TOKEN" \ -H "Content-Type: application/json" \ --data '{"files":["https://example.com/logo.png"]}'
CDN 快取 vs KV 快取
在 Cloudflare,「快取」這個詞常指兩種很不一樣的東西。CDN 快取(也就是本篇)是自動的、以網址為鍵;Workers KV 則是應用層快取,由你自己的程式以 key 讀寫。兩者解決的是不同的問題。
CDN 快取
快取的是整個 HTTP 回應,以網址為鍵,所有使用者共用。由 Cache-Control 與快取規則控制。最適合靜態資源與頁面。
KV 快取
你用自己挑的 key 存任意的值,在 Worker 程式裡讀寫。適合存運算後的 API 結果、設定,與以 key 區分的資料。
自動 vs 手動
內容一經代理,CDN 快取就自動發生。KV 在你的程式呼叫 KV.get、KV.put 之前什麼都不做——邏輯由你掌控。
兩者可以併用
Worker 可以先讀 KV 組出回應,再送出 Cache-Control 讓 CDN 把這個回應快取在邊緣——兩層快取,一條快路。
陷阱與小提示
千萬別快取每位使用者專屬頁面
如果已登入的後台被公開快取,某位使用者就可能看到別人的資料。針對個人化或需驗證的回應,一律送出 Cache-Control: private, no-store。
- 回 200 但沒有 Cache-Control,常會變成 DYNAMIC——設好標頭才會被快取。
- cf-cache-status 是各邊緣據點各自記錄,所以新城市出現一次 MISS 很正常。
- 在檔名加版本(app.v2.js),更新就會產生新的快取鍵,不必特地清除。
- 查詢字串預設會改變快取鍵——/a?x=1 和 /a?x=2 是分開快取的。
- 善用 stale-while-revalidate,讓使用者永遠不必等重新驗證的來回。