Http Cache 快取
Cache Control Setting
🔖 文章索引
1. 設定 HTTP caching
2. HTTP caching 過期後,如何知道檔案是否有更新 ?
3. Content-addressable storage 檔名控制
Browser Cache (又稱 HTTP Cache) 在效能優化上占了很重要一環,雖然設定不關前端的事?! (大部分都是後端在 Server 設定),但只要牽扯到 Client Side 就是跟前端有關了是吧 XD。所以到底什麼是 HTTP Cache ?
想像小明要看 “哈利波特神秘魔法石”、“Clean Code” 這兩本書,若每次都必須去圖書館看那很浪費時間,不如直接借回家隨時想看就看,這樣也節省了每次必須到圖書管的通勤時間。
這邊的小明也就是 Browser,圖書館是 Server ,書則是網路資源。當 Browser 向 Server request 資源時 (例如某張圖片或某個 JS file),若在第一次拿到時就把他先存起來,那下一次 Browser 需要同一張圖片時就可以直接在 Cache 拿,不需要實際發送請求到 Server。
設定 HTTP caching
最常見的有三種 Scenarios: 完全不要快取 no-store
、每次都發 request 檢查確認用的是最新版 no-cache
、以及時間未到不會向伺服器發送請求 max-age
Cache Missing 不要快取
no-store: 每次都發 request 去跟 Server 要資料
- Browser 發送一個 HTTP request 要一張圖 me.jpg
- Server 回傳 me.jpg 給 Browser
- Browser 再次一個 HTTP request 要同一張圖 me.jpg
- Server 回傳 me.jpg
這樣的好處是每次都可以拿到最新的圖片,但若發送 request 要 1 秒,response 3 秒,那五次就必須等 20 秒,相當耗時。
Stale 確保是最新版
no-cache: 雖然會 cache,不過每一次都還是會發 request 問 Server 內容有沒有更新 (revalidate) ,沒更新就用 cache 的
- Browser 發送一個 HTTP request 要一張圖 me.jpg
- Server 回傳 me.jpg 給 Browser
- Browser 將此圖片存在用戶端內。
- Browser 再次一個 HTTP request 要同一張圖 me.jpg
- Server 使用
ETag
(搭配If-None-Match
) 查看圖片是否有更新,沒更新就回傳 304 叫 Browser 直接用 Cache 資料; 有更新就回傳新的 me.jpg
這樣的好處是確保每次都可以拿到最新的圖片,且省掉回傳時間,若發送 request 要 1 秒,response 3 秒,那五次就等約 8 秒,比 no-store 快多了。(當然這邊時間只是舉例,因為去查看有無更新也需要時間,但實際上還是會比回傳時間快上許多)
Valid 時間未到不會向伺服器發送請求
max-age: 若 cache 沒有過期甚至不需要發送 request,直接就拿 Cache 的資料; 若 cache 過期那接下來流程會像 no-cache
- Browser 發送一個 HTTP request 要一張圖 me.jpg
- Server 回傳 me.jpg 給 Browser,且在 Header 塞一個
Cache-Control:max-age=60
與Etag: abcde
欄位,標示這張圖片要緩存 60 秒,過期後再去 Server 重拿。 - Browser 看到回應有
Cache-Control
欄位,就將此圖片存在用戶端內。 - Browser 再次發一個 HTTP request 要同一張圖 me.jpg
Cache 沒過期
- 發現 Browser 已經有 Cache 了,所以連 request 都不需要發了就直接從本地端拿回傳。
Cache 過期 (時間過了 100 秒)
- 發現 Cache 已到期,再請新發送請求,並且帶這從之前
etag
拿到的圖片指紋,發送給 Server。 - Server 使用 ETag (搭配 If-None-Match) 查看圖片是否有更新,沒更新就回傳 304 叫 Browser 直接拿 Cache 資料; 有更新就回傳更新後的 me.jpg
這樣的好處是超快,若 Cache 沒過期那拿五次只需要 4 秒! (比前面的 20 秒跟 8 秒都快),若 assets 超多那使用者體驗也會差非常多。但強制 cache 也有一個問題,就是過期時間若沒設定好就無法拿到最新的資料 (不過這有其他解決方法之後會提)。
Stale While Revalidate 稍微忍受過期的 response
Cache-Control: max-age=1, stale-while-revalidate=59<秒數>
表示在這 1 秒內直接拿 Cache,1–60 秒內可以重複使用這個過期的內容,但同時從背景做 revalidate 的動作,下一次才會拿到新的。61 秒後會重種發送 request
HTTP Status Code
Server 回應有兩種
- 304 (Not Modified): 通常發生在 server 檢查後向 Browser 表示可以繼續使用原本 cache
- 200 (ok): 只能代表成功回應,不能代表是從 Server response 還是 browser cache 來,但從 browser cache 來的會附註 disk cache / memory cache
HTTP caching 過期後,如何知道檔案是否有更新 ?
上面有提到當 Cache 過期就會重新發送 request 給 Server,看檔案是否有更新(revalidation) ,那 Server 是如何判斷的呢?
通常有兩種方法,一種是看修改時間 Last-Modified
; 另一個更準確是看檔案內容是否有修改 ETag
。
修改時間
Last-Modified (搭配 If-Modified-Since)
// Server Responce
Last-Modified: 2017-01-01 13:00:00
Server 回傳 Response 時會加一個 Last-Modified
Header,表示這個檔案上一次的更改時間,Browser 會把這個檔案存進快取並標記這個時間,下一次發 request 時就會以 If-Modified-Since
來跟 Server 要,若時間一樣表示檔案沒更新,Server 就會回一個Status code: 304 (Not Modified)
,代表可以繼續沿用快取的這份檔案; 若時間對不上代表檔案有更新那 Server 就回傳新檔案。
// Browser Request
GET /me.jpg
If-Modified-Since: 2017-01-01 13:00:00
但使用更改時間決定檔案變動不是 100% 準確,因為有可能檔案內容根本沒變但直接存擋,所以使用 ETag 會更為準確。
檔案內容更動與否
ETag (搭配 If-None-Match)
// Server Responce
Etag: abc
Server 回傳 Response 時會加一個 Etag
Header,可以想成同樣檔案 Etag
就相同,更動過檔案 Etag
就會不同。Browser 會把這個檔案存進快取並標記Etag
,下一次發 request 時就會以 If-None-Match
詢問 Server 要有沒有新檔案,有的話就回傳新的,沒有的話就只要回傳 304 就好了。
// Browser Request
GET /me.jpg
If-None-Match: abc
Content-addressable storage 檔名控制
Hash of the stored data as the identifier and the address
main.asfdfdf.js // version 1
main.fklflm.js // version 2
Cache-Control: max-age=31536000, immutable
要如何讓檔案不更改就不要發 request 只讀 Cache 就好了呢? no-cache
做不到因為他每次都要先發 request; max-age
也沒有辦法判斷檔案有沒有更新,所以有人就提出 Content-addressable storage 概念,其實跟 Etag
概念有點像,只是把 hash 實做在 index.html 裡。
<head>
<link rel='stylesheet' href='style-434hdjx.css'></link>
<script src='main-asfdfdf.js'></script>
</head>
index.html 是 no-cache
,而其他 assets (css, js) 都設定快取一年 Cache-Control: max-age=31536000
,所以每一次 request index 都會去檢查是否有更新,若看到 js 或 css 名稱變了就會重新下載,若沒有更動就沿用快取檔案,immutable
表示的是這個檔案不可能會改變 ,所以完全不需要 revalidate (etag)。