Skip to content

HTTP Cache:快取指令與應用

此文章是 FrontendMaster 上的 Advanced Web Development Quiz 課程筆記

問題

將以下快取的指令與其定義配對

[1] no-cache
[2] stale-while-revalidate
[3] no-store
[4] private
[5] must-revalidate

[a] validates a cached response with the origin server before using it, even if it's still fresh
[b] serves stale content while validating the cached response with the origin server
[c] doesn't cache any part of the request or response
[d] prevents caching on shared caches
[e] validates a stale response with the origin server before using it

說明

Cache (快取) 就是讓瀏覽器記住之前下載過的東西 (圖片、CSS、JS、甚至 API 資料),下次需要時直接拿出來用,不用再跟伺服器要一次

優點:

  • 更快的載入速度: 不用等網路傳輸,直接從本地拿
  • 省流量: 使用者的網路流量不會浪費在重複下載相同檔案
  • 減輕伺服器負擔: 伺服器不用一直回應相同的請求

對前端來說 Cache 分成兩類

1. 瀏覽器自動管理的 Cache (HTTP Cache)

由瀏覽器根據伺服器回傳的 HTTP header 自動處理,當瀏覽器要取得資源時,會經過以下流程:

  1. 請求 style.css
  2. 瀏覽器:我的記憶體或硬碟有這個檔案嗎?
    1. 有,而且還沒過期:直接用,不發請求
    2. 有,但過期了:發請求問伺服器「這個還能用嗎?」
    3. 沒有:發請求下載新的

一切都是自動的,前端不需要寫任何的 JavaScript,只需要在伺服器設定 header 即可

2. 前端主動控制的 Cache

  • localStoragesessionStorage:用 JS 手動存資料
  • Service Worker: 可以攔截網路請求,自己決定要回快取還是發請求,用於 PWA 離線功能
  • IndexedDB: 可以存大量結構化資料的本地資料庫

HTTP Cache

HTTP Cache 是透過 HTTP Response Headers Cache-Control 來控制的,這些 header 由伺服器端決定並送給瀏覽器,瀏覽器再根據這些資訊決定快取行為

在瀏覽器上的 DevTools 裡面的 Network,點擊任何請求看 Response Headers 可能會看到以下的資訊

Cache-Control: public, max-age=31536000, immutable
Cache-Control: no-cache, must-revalidate
Cache-Control: private, max-age=0, no-store

每個指令會被逗號分隔呈現 Cache-Control: <directive>, <directive>, <directive>, ... 的樣子

  • 這些指令是組合使用的,不是只能選一個
  • 多個指令會一起影響快取行為
  • 如果有衝突,通常是「最嚴格」的那個生效

而決定使用快取的流程:

  1. 是否使用本地快取?
    • 若資料還是 fresh > 直接用,不打 server (強快取)
    • 若已經過期 (stale),或被要求一定要驗證 > 進入下一階段
  2. 要不要跟原始伺服器...
    • 帶上 ETag/ Last-Modified 等 header
    • 若 server 說沒變:回 304 瀏覽器用本地快去 (revalidation)
    • 若 server 說有變:回 200,下載新內容

題目所提到的 no-cachemust-revalidateno-storeprivatestale-while-revalidate 都是在控制「這兩個階段要怎麼走」。

補充說明

ETag (Entity Tag)

  • 是資源內容的「指紋」或「版本號」
  • 是獨立的 header
  • 通常是伺服器對檔案內容做 hash,如果內容變了,hash 就變,比 Last-Modified 更精準

Last-Modified 記錄資源最後修改的時間

  • 記錄資源最後修改的時間
  • 是獨立的 header
  • 只能精確到秒,如果你在同一秒內改了兩次檔案,可能偵測不到
  • 時間可能不準,不同伺服器的時鐘可能有誤差
  • 某些情況會失效,檔案內容沒變但時間戳變了(例如重新部署)

no-store

完全不理會 HTTP Cache,每次都直接打伺服器拿一份新的資訊

適合用在金融、醫療、個資這種類型的資料

情境

第一次請求

Response Headers:

HTTP/1.1 200 OK
Cache-Control: no-store
第二次請求

因為完全沒有快取可用,所以行為和第一次一模一樣,這次的內容也不會被存到快取。

no-cache

可以快取,但每次使用快取之前都會詢問 server (revalidate)

  • 搭配 ETag / Last-Modified,讓流量最小化
  • 適合 HTML、重要但不算敏感、常更新的資源

情境

第一次請求

Response Headers:

HTTP/1.1 200 OK
Cache-Control: no-cache
ETag: "abc123"
Last-Modified: Wed, 10 Dec 2025 10:00:00 GMT

瀏覽器會存快取,但標記成「每次用前要驗證」。

第二次請求

Response Headers:

GET /index.html HTTP/1.1
If-None-Match: "abc123"
If-Modified-Since: Wed, 10 Dec 2025 10:00:00 GMT
  1. 伺服器回應情況 1:沒變
    • Network: Status=304, Size=很小(只有 header)
    • 瀏覽器拿之前的 body 出來用。
HTTP/1.1 304 Not Modified
ETag: "abc123"
  1. 伺服器回應情況 2:改變
    • 下載新 body,更新快取。
HTTP/1.1 200 OK
ETag: "def456"

must-revalidate

  • 在 fresh 的狀態下可以直接用快取 (強快取),一但過期 (stale) 就必須跟 server 驗證 (revalidate)
  • 不能接受「過期資料」的資訊(價格、庫存),但在 fresh 時可以使用快取

情境

第一次請求

Response Headers:

HTTP/1.1 200 OK
Cache-Control: max-age=60, must-revalidate
ETag: "v1"
第二次請求 (在 60 秒內)

和「強快取命中」相同:

  • 直接從快取讀,不發 request。
  • Network 顯示 200 (from disk/memory cache)。
第三次請求 (超過 60 秒)

一定會發「協商請求」,就像 no-cache 那樣帶上 If-None-Match/If-Modified-Since。

回應不是 200 就是 304。

stale-while-revalidate

過期後的一段時間內,可以先用舊的回應使用者,同時在背景去更新快取。

  • 時間在 0 ~ max-age 間:直接使用快取 (fresh)
  • max-age ~ max-age + stale-while-revalidate: 可以用快取,但要背景 revalidate
  • 超過以上範圍: 不能再用快取,必須向 must-revalidate 先去驗證

適合對時效性沒這麼敏感的內容:部落格、文章列表

情境

第一次請求

Response Headers:

HTTP/1.1 200 OK
Cache-Control: max-age=60, stale-while-revalidate=300
ETag: "v1"
第二次請求 (0~60 秒)

和「強快取命中」完全一樣:直接快取,不打網路

第三次請求 (60~360 秒)
  • 前端的感受:

    • 使用者 → 立刻拿到舊的快取(幾乎像強快取)
    • 背景 → 瀏覽器同時對伺服器發一個「協商請求」(帶 If-None-Match 等)
  • 如果伺服器回 304 → 更新 freshness,下次就還是當 fresh 用

  • 如果伺服器回 200 → 把新內容寫進快取,但這次畫面還是舊的,下一次才會用到新內容

private

這主要影響瀏覽器以外的中間層快取 (CDN、proxy)

  • public: 任何快取都可以存(瀏覽器、CDN、proxy),CDN 等共享快取也可以把「第一次回應」存下來,幫很多使用者當「第二次」用
  • private: 只有使用者端的私有快取 (瀏覽器) 可以存

適合個人化資料 (個人儀表板、帳戶頁),可以讓瀏覽器快取,但不想讓 CDN 共享給別人

Response Headers:

Cache-Control: private, max-age=0, no-cache

同一個資源,世界各地的使用者是各自有自己的第一次/第二次,還是大家共用 CDN 的快取

參考

最後更新時間:

0 %
MIT Licensed | Copyright © 2025-present Wen-Hsiu's Blog