前幾天說到 WebForm 版 js/css 自帶版本參數元件,讀者 yoyo 提了一好問題:HTTP 針對靜態檔已有 ETag、Last-Modified、If-Modified-Since、If-None-Match 等快取管理機制(延伸閱讀:IIS HTML 檔 Cache 行為觀察,何需自己處理?

對於這個問題我的理解停在:1) 經驗裡 HTTP 快取管理靠不住,還是自己控制比較安心 2) ASP.NET MVC Core 跟許多前端框架也都是採用 URL 加上 ?v=<file_hash> 的做法,必有其道理,拿香跟著拜準沒錯。(?v=<file_hash> 的專業術語叫版本化網址(Versioned Url),串接的雜湊參數叫指紋 Fingerprint,參考)

但既然被問到,這篇文章就來鑽深一寸,談談為什麼版本化網址優於 HTTP 快取控制,進而成為當前主流。

由網站取得 js/css 時在 Response Header 附上 ETag (相當於檔案雜湊,內容異動後會改變) 跟 Last-Modified 最後更新時間;瀏覽器儲存成快取時一併保存 ETag 及更新時間,使用快取前,在 GET Request Header 加入 If-None-Match 及 If-Modified-Since 將 ETag 跟最後更新時間交給網站比對,若檔案未異動回傳 HTTP 304 告知瀏覽器可使用快取內容;否則以 HTTP 200 回傳新版內容。聽起來也是不錯的運作方法,但有以下缺點:

  1. 瀏覽器及伺服器都需要支援這種做法,若中間串接的 WAF/Reverse Proxy/CDN 等機制有不同實作邏輯,可能出現預期外的結果。
  2. 即使檔案未變可使用記憶體或檔案快取,還是要產生一次 HTTP 來回傳輸進行確認,對頻寬及伺服器都屬無謂損耗,不利於效能。
  3. 最嚴重的一點,各家瀏覽器的快取原則可能不一致,這也是印象中這套做法不如版本化網址可靠的主要原因,下面用 Chrome/Edge 實測說明。

我做了一個簡單網頁,網頁 index.html 會載入 test.js、site.css 及 logo.png,四個都是靜態檔,但可觀察到 Chrome/Edge 不同的處理原則。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <script src="./test.js"></script>
        <link rel="stylesheet" href="./site.css" />
    </head>
    <body>
        <img src="./logo.png" />
    </body>
</html>

單獨瀏覽 png/css/js,每次按 F5 會發請求確認,得到 304 後再用快取,這符合我們的預期。

但當 css/js/png 被包含在網頁裡,行為則不同。

上圖紅框的 Request 來自按 Ctrl-F5 強制重新讀取,四個 GET 都會由網站重新下載內容,結果為 200。黃框是按 F5 重新整理的結果,只有 index.html 有送出 HTTP Request,附上 If-Modified-Since 及 If-None-Match(下圖橘框),收到 HTTP 304 後再使用快取;其餘 test.js/site.css/logo.png 則未問網站,直接用快取(下圖黃框)。

由此可知,Chrome/Edge 載入網頁時會直接由快取讀取 js/css,不會跟伺服器確認檔案是否異動。期待瀏覽器會與伺服器自行協調,在檔案異動時自動更新快取的夢想宣告破滅。

猜想,調整網站或瀏覽器某些設定或許可以改變這個行為(註:見文末更新),但使用版本化網址不依賴瀏覽器或伺服器種類,在各種環境下都能正確運作,又可省下每次確認檔案是否更新的 HTTP 304 請求,評估下來是更強韌且有效率的設計,應是它成為前端主流的原因吧。

【2023-11-23 更新】透過 Cache-Control: max-age=0Cache-Control: no-cache 可強迫求瀏覽器每次使用快取前必須向伺服器確認,得到 304 回應才使用。關於 HTTP Cache 運作細節,推薦這篇:循序漸進理解 HTTP Cache 機制 by 胡立

Explaining why versioned url is better than ETag, If-None-Match HTTP cache control.


Comments

# by yoyo

感謝黑大詳細的測試,我這邊用IIS 10測試 設定 HTTP 回應標頭/設定一般標頭\勾選 網頁內容到期\立即 or 之後 0秒 Dev Tool查看 Response Headers 會有Cache-Control設定值 使用Chrome瀏覽包含PNG的HTML檔,PNG也是會回傳HTTP 304的 而且更換PNG圖片後,F5馬上更新 設定 HTTP 回應標頭/設定一般標頭\不勾選 網頁內容到期 與你測試結果相同 Dev Tool查看 Response Headers 無Cache-Control設定值 PNG會回傳HTTP 200 (memory cache) 而且更換PNG圖片後,F5不會更新,需Ctrl+F5才會更新圖片

# by yoyo

補充說明: 更改IIS HTTP 回應標頭設定後,Chrome需Ctrl+F5才會生效

# by Jeffrey

to yoyo,感謝分享測試心得。所以還是版本化連結好,簡單粗暴,不管 IIS 怎麼調快取設定都不會壞掉。

Post a comment