跳至主要内容

Cache control

Cache control

當有使用者造訪網站時,他們的瀏覽器會將某些資源(例如影像和網站資料)儲存在稱為快取的存放區中。
若重新造訪同一網站時,快取控制會設定規則,以確定該使用者是將從其本機快取中載入這些資源,還是瀏覽器必須向伺服器傳送請求以獲取新資源。


主要就是針對 Cache 來控制瀏覽器如何與快取的內容互動
有分成以下幾種


Not Cacheable

  • Cache-Control: no-cache
  • Cache-Control: no-store

no-cache:

  • 會快取,但每次請求前都先向伺服器用 ETag 檢查是否有更新檔案,確認這些資源自上次緩存以來是否有更改。
    如果資源未更新,則伺服器回傳 304 (Not Modified),則可以從本地快取中提供,而不是重新下載 如果資源已更新不一樣,就會重新取得新資源
  • 等同於 max-age=0, must-revalidate
  • 強調的是在重用快取之前的驗證

no-store:

  • 不要讓瀏覽器快取
  • 表示禁止儲存快取,每次都必須跟伺服器要求新檔案,適用於私人或機密資料。

Immutable


Immutable

這些資源可以被瀏覽器無限期地存儲,因為它們永遠不會改變。

Cache-Control: public, max-age=31536000, immutable
提示

有些檔案的名稱中會加上一串 SHA-hash (app.34dgew22.js)。這個檔案內容一定不會有變更,因為當檔案變更的時候,就會產生新的 hash,使用者就會重新下載最新的檔案

備註:

next.js 相關用法: https://nextjs.org/docs/app/building-your-application/deploying#automatic-caching:~:text=Next.js%20application.-,Automatic%20Caching,-Next.js%20sets


Time-restricted


max-age = 0

  • 表示快取的資源立即過期,並且每次請求都需要向原伺服器進行確認。這保證了每次請求都獲得最新的資料。再搭配上Etag來使用,就可以保證只會下載到最新的 Response。
  • 強調的是資源立即過期並且每次都需要驗證

max-age: 300

  • 設定 cache 的最大時間,超過這個時間儲存被認為是過期(秒)Expires。
  • 在 client 端 cache 5 分鐘

s-maxage: 300

Cache-control: max-age=3600, s-maxage=86400
  • s-maxage 的 s 表示 shared 的意思,專門用於共用快取(如 CDN)。
    上面設定針對 client(browser) 的有效時間會是一小時,但 CDN 則會是一天。

must-revalidate

  • 表示如果檔案過期後,瀏覽器一定要先向 server 詢問。
  • 防止在斷網、Server 掛掉時,繼續使用 stale responses 。

通常,must-revalidate 與 一起使用 max-age。

Cache-Control: max-age=604800, must-revalidate
此範例將允許瀏覽器使用快取的資源長達一年,之後必須透過伺服器重新驗證

Revalidated


max-age = 1, stale-while-revalidate= 60

在發送請求前會先檢查  max-age  來判斷 cache 是否已經過期,

  • 如果還沒過期,就直接拿瀏覽器的快取(不會實際像 server 發出請求);
  • 但若它判斷已經過期,同時有設定  stale-while-revalidate  的時間(且在該時間區間內),會判定該 cache 已經過期,雖然如此,會先拿原本的 cache 來作為回應,但「同時」又在背景向伺服器發送請求(revalidation request),以此產生新的 cache 供未來使用。
  • 內容不會改變的檔案,完全不需要 revalidate

情境:

  • 0 ~ 1 秒時:由於使用 max-age=1,因此在這段時間內向伺服器發送請求時,瀏覽器會直接取用保存過的 cache 當作回應(fulfill browser request),而不會實際向 server 發送請求。
  • 1 ~ 60 秒時:由於超過了 max-age 定義的時間,因此 cache 已經過期,但因為有使用 stale-while-revalidate ,因此瀏覽器仍會先以 cache 當作請求的回應(fulfill browser request),但同時在背景向伺服器查詢有無新的畫面。如果有,就會更新畫面上的資料。
  • 61 秒之後:超過了 max-age 和 stale-while-revalidate 所定義的時間,因此 cache 已經過期,也不會使用 swr 的機制,而是直接向伺服器發送請求,取得伺服器最新的回應

備註: private & public

  1. private: 此  cache 是私有的,只有 client (browser) 可以儲存   cache , 而不能由中繼代理程式(如 CDN 或代理)快取,常用於個人化敏感資料或像是 api response。
  2. public: 此  cache  為公有共享的,中間傳輸的   ISP, proxy 及   CDN 服務商都可以儲存   cache,常用於公用的靜態檔案,像是 js, css 及圖片等 asset。
    • e.g. public, max-age=14400

當 Cache 過期時

  • 當 cache 過期了,加上如果資源(e.g. Google 的 Logo)一年後仍然沒有變,瀏覽器可以繼續使用已經快取的圖片,而伺服器只需告知瀏覽器快取的圖片仍然有效,無需重新下載。
  • 這裡可以透過 Last-ModifiedETag 搭配使用
信息

ETag 標頭的優先級高於 Last-Modified


Last-Modified

  • 表示資源最後一次修改的時間
  • 在 server 回傳  Last-Modified  的 HTTP Header 後,瀏覽器在後續的 Request Header 中都會自動帶入  If-Modified-Since  的欄位,這個欄位的值會是上一次發送請求時  Last-Modified  回傳的值。
  • server 即可根據  If-Modified-Since Header 的時間來決定要不要回傳新的內容給瀏覽器。
  • 當 client 端帶著If-Modified-Since 時, server 透過 Last-Modified 去判斷是否cache 為最新的,如果是就會回傳 304 http code
  • Last-Modified: 出現在伺服器回應給瀏覽器的 response header 裡,告訴瀏覽器這個檔案上次更改是什麼時候。
    If-Modified-Since: 出現在瀏覽器發出請求的 resquest header 裡,用來跟伺服器確認檔案在某個時間點後是不是有經過更改。 last-modified

上述提到的是檔案是否被編輯,但編輯時間其實是電腦上的檔案修改時間。 即使開啟檔案未作更改,儲存後編輯時間也會更新,內容卻可能未變。

相對於編輯時間,使用「檔案內容是否更改」來判斷是否更新快取更理想。 Etag Header 正是基於此,它像是檔案內容的哈希值,相同內容產生相同 hash 值,不同則產生不同 hash 值,以此判斷內容是否變更。


ETag

  • ETag(Entity Tag)是一種唯一標識符,用於識別一個資源的特定版本。
  • 在 server 回傳  Etag  的 HTTP Header 後,瀏覽器在後續的 Request Header 中都會自動帶入  If-None-Match  的欄位,這個欄位的值會是上一次發送請求時  Etag  回傳的值。
  • 主要就是針對 revalidate
  • server 即可根據  If-None-Match Header 的值來決定要不要回傳新的內容給瀏覽器。

舉例: 第一個 Response 可能是這樣:

Cache-Control: max-age=0
Etag: abcdef

重新整理一次,瀏覽器發出這樣的 Request:

If-None-Match: abcdef

如果檔案沒有變動,Server 就會回傳:304 Modified,有變動的話就會回傳新的檔案並且更新 Etag。如果是使用這種方式,其實就是「每一次造訪頁面都會發送一個 Request 去確認有沒有新的檔案,有的話就下載更新,沒有的話沿用快取裡的」。

etag1


網頁快取策略

  • 靜態資源(圖片): 使用 Cache-Control: max-age=xx
  • 有版本的檔案: 使用 Cache-Control: max-age=31536000, immutable
  • 可能變更的非版本化檔案: 使用
    Cache-Control: max-age=604800, stale-while-revalidate=86400
    ETag: "<file-hash-generated-by-server>"
  • 網頁:
    • 網站首頁雖然也不常會變動,希望只要一變動,使用者就能夠馬上看到變化: Cache-Control: max-age:300
    • 個人畫頁面: Cache-Control: max-age:300, private

策略參考


Next.js 快取策略

  1. ISR 頁面: s-maxage=60, stale-while-revalidate
  2. 動態頁面: private, no-cache, no-store, max-age=0, must-revalidate
  3. 有版本的資源: public, max-age=31536000, immutable

參考


Memory Cache vs Disk Cache

參考: https://ithelp.ithome.com.tw/articles/10276125

當深入探討 Chrome 瀏覽器的快取機制時,你可能會偶爾遇到除了常見的from disk cache外,還有一種叫做from memory cache的標記。

這個 memory cache 可能不是所有瀏覽器都有的,可以視為 Chrome 的一項特殊實作。

簡單來說,memory cache 是將資源存儲在內存(RAM)中,這讓存取速度更快於儲存在硬碟的 disk cache。 然而,這種快取的資料是揮發性的,一旦關閉瀏覽器,所有在內存中的資料就會消失。

總結來說,Memory Cache 提供了速度上的優勢,適合快速存取的情況,而 Disk Cache 提供了持久性和大容量的優勢,適合長期和大量數據的存儲。

從前端開發的視角來看,了解瀏覽器如何選擇哪些資源放入 memory cache 是個有趣但複雜的話題。 雖然沒有明確的規則,我們知道使用了 resource hint 如 preload 和 prefetch 的資源更可能被存到 memory cache。 當然,由於記憶體空間遠小於硬碟,不可能所有資源都放入 memory cache,這需要一個有效的空間管理策略,幸好這部分瀏覽器都幫我們處理好。

信息

優先順序: memory cache -> service worker cache -> disk cache (browser cache)

其他

用 cURL 檢查 HTTP Header

curl -I xxx

cURL

另外一個方式就是透過好用的 CLI Tool cURL,他除了可以獲得從 Server 端傳送而來的 HTTP Header 之外,也可以自己取組出想要送到 Server 端的 HTTP Header


在網頁中快速瀏覽 cache-control 設定

cache


需注意 stale-while-revalidate 在 safari 上面並不支援。 會默默地忽略該配置值,並使用 max-age 搭配 ETag, 讓瀏覽器在 max-age 過期後,使用 ETag 進行異步重驗證,以確保資源的新鮮度。

https://caniuse.com/?search=stale-while-revalidate


結論

cache


參考資料:

  1. https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control
  2. https://blog.camel2243.com/2018/09/23/http-http-header,-cache-control-expires-用法說明/
  3. https://blog.techbridge.cc/2017/06/17/cache-introduction/
  4. https://www.zhihu.com/question/64201378
  5. https://www.cloudflare.com/zh-tw/learning/cdn/glossary/what-is-cache-control/
  6. https://pjchender.dev/webdev/note-http-cache/
  7. https://medium.com/starbugs/%E6%80%8E%E9%BA%BC%E7%B6%B2%E9%A0%81%E6%94%B9%E5%AE%8C%E9%82%84%E6%98%AF%E9%8C%AF%E7%9A%84-%E4%B8%80%E6%AC%A1%E6%90%9E%E6%87%82-http-cache-%E6%A9%9F%E5%88%B6-a39a421df6c9
  8. https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Caching
  9. https://ithelp.ithome.com.tw/articles/10276125
  10. https://simonhearne.com/2022/caching-header-best-practices
  11. https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Expiration.html#stale-content