🎯 什麼情境該想到我
當使用者「重複造訪網站卻還是慢」、資源每次都重新下載時。
⚙️ 怎麼用
- 設快取標頭:對靜態資源加長
Cache-Control/Expires,讓瀏覽器重複造訪直接用本地副本。 - 內容雜湊檔名(cache busting):檔名帶 hash(如
app.9f3c.js),內容一改檔名就變 → 可放心設「永久快取」,更新自動失效。(本站 Quartz 就是這樣。) - 善用多層快取:瀏覽器 → CDN → 反向代理 → 應用快取,越外層命中越省。
- 壓縮與合適格式:圖片用適當格式/尺寸,別傳過大資源。
★ Cache-Control 的五個關鍵指令
| 指令 | 意思 | 常見誤解 |
|---|---|---|
max-age=N | N 秒內直接用快取,不問伺服器 | |
no-cache | 可以存,但每次使用前都要向伺服器驗證 | 常被誤以為是「不要快取」 |
no-store | 完全不要存 | 真正的「不要快取」;用在含個資的回應 |
immutable | 在 max-age 期間內容絕不會變,連重新整理都不用驗證 | 只能搭配內容雜湊檔名使用 |
private / public | 只能存在瀏覽器/CDN 等共用快取也可以存 | 含使用者資料的回應一定要 private |
★ 兩種資源、兩種策略
| 資源 | 例子 | 標頭 | 更新怎麼生效 |
|---|---|---|---|
| 帶雜湊的靜態檔 | app.9f3c2a.js、style.a81b.css | Cache-Control: public, max-age=31536000, immutable | 內容改變 → 檔名改變 → 等於新檔案 |
| 入口 HTML(和其他不能改名的) | index.html | Cache-Control: no-cache + ETag | 每次驗證;沒變就回 304 Not Modified(不傳內容) |
這個組合的邏輯:HTML 每次都驗證(很小,304 只要一個往返),HTML 裡引用的帶雜湊檔名則永久快取。部署新版本時,新的 HTML 指向新的檔名,舊檔案自然不再被使用。不需要手動清快取。
★ 驗證的機制:ETag 與 304
- 第一次請求:伺服器回
200+ETag: "abc123"。 - 之後的驗證:瀏覽器送
If-None-Match: "abc123"。 - 內容沒變:伺服器回
304 Not Modified,沒有 body → 省下傳輸量,但仍然有一個往返。 - 內容變了:回
200+ 新內容 + 新 ETag。
所以 no-cache + ETag 省的是頻寬,max-age 省的是往返。行動網路上一個往返可能要幾百毫秒,這就是為什麼帶雜湊的資源要用 max-age + immutable。
★ Go 的做法
// 帶雜湊的靜態檔:永久快取
fs := http.FileServerFS(assets) // Go 1.22+;assets 是 embed.FS 或 os.DirFS
mux.Handle("GET /assets/", http.StripPrefix("/assets/",
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
fs.ServeHTTP(w, r)
})))http.FileServer會自動處理If-Modified-Since/Last-Modified,並在內容沒變時回 304。- 但用
embed.FS嵌入檔案時,檔案的修改時間是零值,Last-Modified機制不會生效 → 靠檔名雜湊 +immutable,或自己計算 ETag。
★ 多層快取的失效順序
瀏覽器 → CDN → 反向代理 → 應用程式快取 → 資料庫。失效要從內往外:先更新資料庫,再清應用程式快取,最後清 CDN。反過來做,外層清完後可能馬上又從內層拿到舊資料再快取一次。
stale-while-revalidate=N:過期後的 N 秒內,先回傳舊的快取給使用者,同時在背景重新驗證——使用者不用等,資料最多舊 N 秒。適合「稍微舊一點沒關係」的內容。
🧪 我實際套用的紀錄
- 2026-07-15:(待填)
⚠️ 注意
-
快取最難的是「失效」;用內容雜湊檔名可一併解掉「更新了卻還吃到舊檔」的問題。
-
CDN 快取了錯誤回應:後端短暫出錯回了 500 或空白頁,如果沒有正確的
Cache-Control,CDN 可能快取它並發給所有使用者。錯誤回應要明確設no-store。 -
Vary標頭:同一個網址會依照Accept-Encoding、Accept-Language回不同內容時,要設Vary,否則 CDN 可能把 gzip 版本發給不支援的客戶端,或把中文版發給英文使用者。
🔗 相關工具
- 工具-前端效能優化規則 —— 快取只是其中一條規則;跳過去拿完整清單,判斷該先動快取還是先減請求數
- 工具-透明多級分流 —— 多層快取的上游架構:瀏覽器 → CDN → 反向代理這條路徑本身該怎麼擺