前端效能優化 Day17 X 初探快取 & HTTP Caching
本章節重點 : Server 跟瀏覽器之間的 Cache 機制 => HTTP Caching
什麼是 Cache?
Image Not Showing
Possible Reasons
- The image was uploaded to a note which you don't have access to
- The note which the image was originally uploaded to has been deleted
Learn More →
- 提供一個暫存可能使用到資料的額外儲存空間
- Cache 是避免 client 和 sever 提出請求的一種方式
- Response 的再利用性(Reusability): 瀏覽器可以在一段時間內重複使用已經快取的回應,而不需要再次向伺服器發送請求
適合被快取的資料
- 很常被使用到
- 資料不常變動
為什麼需要 Cache
節省流量,節省時間,減少資源的損耗
Cache Hit vs Cache Miss
- Cache Hit : 指的就是當發出請求時在快取就找到想要的資源
- Cache Miss : 指的就是快取中找不到想要的資源,必須再回去跟 origin server 拿資料
- 效能優化的角度 : 提高 Cache Hit Ratio,降低 Cache Miss Ratio
快取的種類
Public 與 Private 的快取
- 這個 response 能不能被不同 users 共享

- 公有快取 : 指快取伺服器上存的回覆能給好幾個不同的請求者服務。
- 私有快取 : 相對只會服務一個請求者,這台電腦的使用者可以使用快取的資源。
HTTP Caching

- HTTP Cache 是避免瀏覽器向伺服器發送不必要請求的一道防線
- 要啟用 HTTP Cache 需要伺服器端與瀏覽器端事先經過協商,瀏覽器與伺服器透過在 HTTP Request 與 Response 的 header 帶入一些資訊來協商快取的機制
Expires
-
HTTP 1.0 舊版本的方式,伺服器在 response header 中可以加入 Expires 字段
-
機制 : 取決於瀏覽器「現在的時間」是否有超過 Expires 中指定的過期時間
-
結果 :
- 沒有超過 =>瀏覽器「不會發送任何 Request」直接從的 Cache 拿資料
- 超過 => 發起 network request

「Status code 200 (from disk cache)」,代表這個 Request 其實沒有發出去,Response 是直接從 disk cache 裡面拿的。
-
問題 : 根據使用者電腦本身的時間來決定過期與否 => Cache Miss
Cache-Control 與 max-age
-
為解決 Expires 的問題
-
HTTP 1.1 時推出了 Cache-Control 這個 header
-
機制 : 使用者在收到這個 response 的 60 秒內,如果再對相同資源發出請求,就會得到快取的版本,如果超過 60 秒後才對這個資源發出請求,則會發出一個新的 network request。
-
範例: 觀察 Google Logo 檔案的 Response header

- max-age 設定成 31536000 秒(365 天)
- 在一年之內造訪這個網站,都不會對 Google logo 這張圖片送出 Request,而是會直接使用快取 : Status code 200 (from memory cache)
Expires跟max-age優先性?
根據RFC2616的定義:
If a response includes both an Expires header and a max-age directive, the max-age directive overrides the Expires header, even if the Expires header is more restrictive
max-age會蓋過Expires。因此現在的快取儘管兩個都會放,但其實真正會用到的是max-age。
tips :
- Expires 和 Cache-Control 這兩個 Header 都是在關注一個 Response 的「新鮮度(freshness)」
- Expires 給定絕對時間,Cache-Control 通常會搭配 max-age 給定相對時間
過期了,然後呢?
「過期了不代表不能用」
Last-Modified & If-Modified-Since
- HTTP 1.0 就被提出的比較舊的解決方案
- Last-Modified : 出現在伺服器回應給瀏覽器的 response header 裡,告訴瀏覽器這個檔案上次更改是什麼時候。
- If-Modified-Since : 出現在瀏覽器發出請求的 resquest header 裡,用來跟伺服器確認檔案在某個時間點後是不是有經過更改。
-
機制:
-
檔案更新 => 瀏覽器會收到一份新的檔案。
-
檔案未更新 => Server 回一個Status code : 304 (Not Modified),代表可以繼續沿用快取的這份檔案。

-
問題 : 根據檔案的編輯時間來做判斷的
Etag & If-None-Match
(補充) 避免 Mid-Air Collisions
-
目標:使用 ETag 避免多人同時編輯的情況
-
流程:當從 view mode 進到 edit mode 時,可以先紀錄當前頁面的 Etag 值
等到修改完成時透過 If-Match 這個 header 檢查 Etag 是否一樣
ETag 值不一致,表示發生共編 => 伺服器回應 412 狀態碼,代表前置條件失敗(Precondition Failed),告知使用者需要重新編輯
tips:

- 選擇適合的 cache policy 是非常重要
- 快取沒過期 : 直接拿不會發出網路請求
- 使用 Etag 或是 If-Modified-Since 的機制 : 會發出網路請求,只是網路封包大小通常會比真的跟伺服器抓取資料還要小許多
不要快取怎麼辦?
含有一些機密資料的頁面
Cache-Control: no-store
代表不要任何快取
Cache-Control: no-cahce
代表此 response 必須經過驗證才能使用
不要任何快取是 no-store 不是 no-cahce!
Cache-Control: private
代表此 response 只可以被瀏覽器儲存起來
Cache-Control: public
代表此 response 可以被任何快取軟體儲存起來,例如 reverse proxy 的快取、瀏覽器的快取
no-cahce 的應用 - 首頁的快取策略
-
目標:「把頁面快取起來,但只要首頁一變動,就能夠立刻看到新的頁面」
[解法1] Cache-Control: max-age=0
(代表這個 Response 0 秒之後就會過期) 再搭配 Etag
來使用 :
第一個 Response:
重新整理,瀏覽器發出 Request:
- 檔案沒有變動,無需發出 Request,Server 回傳:304 Modified,沿用舊檔案
- 檔案有變動,回傳新的檔案並更新 Etag
[解法2] Cache-Control: no-cache
:
- 無論檔案有沒有變動,每次都會發送 Request 去確認
max-age=0 允許快取在重新驗證後使用舊的回應,而 no-cache 則要求快取必須重新驗證回應,確保每次都使用最新的數據。
Cache-Control: no-store v.s. Cache-Control: no-cahce
- 案例 : A 網站是使用 Cache-Control: no-store,B 網站是使用 Cache-Control: no-cache
- 假設 index.html 有 100 kb
- 假設封包大小 1kb
- 結果:
- 造訪了十次 A,累積的流量就是 1000kb
- 造訪了十次 B,但 B 第十次才更新,累積流量就是 9 (前九次 Server 只會回傳 Status code 304) + 100 = 109 kb
達成的目標一致 : 只要網站更新,使用者就能立即看到結果,但是 B 的流量遠低於 A,因為有善用快取策略。
tips:
- max-age=0 跟 no-cache 的差異,SHOULD-revalidate 和 MUST-revalidate
- no-store 跟 no-cache 的差異,永遠不用快取跟永遠檢查快取
Cache Busting
no-cache 無論如何,都會發出 Request。有沒有可能,連 Request 都不發呢?
- 目標:檔案不更新,瀏覽器就不會發 Request,直接沿用快取。檔案一更新,瀏覽器就要立即抓取新的檔案
- 機制: Cache Busting - 為檔案建立一個獨立識別檔名,只要檔名變了,瀏覽器就認為這是一個新的檔案,需要重新跟伺服器抓取(類似把 Etag 機制實作在 HTML file裡面)
- webpack 這些 bundler 都可以做到在打包時加一串 hash 字串到檔名裡,並同時自動更新 HTML 裡面引入的檔名
- 以下方修改 CSS 後重新打包的 HTML 為例 : 只有 CSS 檔名中的 hash 有改變
- 原理: 針對不同的檔案採用不同的快取策略,並且直接用「更換 CSS 或 JavaScript 檔案」的方式強制瀏覽器重新下載
- 策略:
對於 CSS 檔案:快取策略為 max-age=31536000
對於 index.html 檔案:快取策略為 no-cache
所以每一次訪問這個頁面,都會去看 index.html 是否更新,因為引入的 CSS 檔名變了,重新發送 Request
Memory Cache vs Disk Cache

- from disk cache : 表示這些資源來自瀏覽器的快取,並沒有再發出 request,因此載入速度會比第一次來的快。
- from memory cahce : 可以看作是 Chrome 特別實作的一種快取,它把資源存在 memory (RAM) 裡面,所以在效能上會比 disk cache 還要快,缺點是關閉瀏覽器時資料就會被清空。
- 目前沒有明確的定義瀏覽器怎麼決定哪些資源要放到 memory cache 裡,只知道透過 resource hint 例如 preload, prefetch 載入的資源比較有機會被放到 memory cache 中。
memory cahce v.s. disk cache
- 效能與準確性 : memory cahce > disk cache
- 不受 Cache-Control 設定影響:即使資源的 Cache-Control 指令要求不快取,memory cache 也可能會將其存儲起來以提高後續的訪問速度
- 識別特徵不同: 傳統的 HTTP 快取通常根據 URL 或檔案名稱來識別和存儲資源,但 memory cache 會額外考慮資源的 Content-Type 和 CORS 等其他特徵來識別和區分資源
既然速度那麼快,就把全部資源都先存到 memory cache 就好啦?
這是不可能的,記憶體的容量相比硬碟小非常多
(補充) cache 的優先順序(指會先到哪種 cache 找有沒有資料)
memory cache -> service worker cache -> disk cache (browser cache)
結語
- Expires 跟 max-age 是在負責看這個快取是不是「新鮮」
- Last-Modified, If-Modified-Since, Etag, If-None-Match 是負責詢問這個快取能不能「繼續使用」
- no-cache 與 no-store 代表到底要不要使用快取,以及應該如何使用
- 所有透過瀏覽器發送的 HTTP 請求,都會先經過瀏覽器的快取,在這裡會先檢查是否有有效的快取內容可作為回應,如果有的話就直接讀取快取的內容,以減少網路的延遲和傳輸造成的成本
參考文章
https://ithelp.ithome.com.tw/articles/10276125