跟同事討論到:「IIS 在靜態檔案更新時會強制瀏覽器讀取新版本嗎?」

HTTP Header 有不少與 Cache 管理有關,協助瀏覽器用 Cache 減少網路傳輸量,例如:Cache-Control、If-Modified-Since、ETag… 等。要了解這些技術細節,推薦幾篇文章:

IIS 預設藉由 ETag 及 If-Modified-Since 讓靜態內容(HTML、JPG、PNG、GIF、CSS、JS...)平時可以被 Cache,但是檔案只要有更新就重新讀取。知道理論但沒親身觀察過,索性做個實驗證明一下。

實驗使用 Chrome 瀏覽器,關啟 F12 開發者工具 Network 頁籤觀察 HTTP Request 及 Response,並調整設定:取消 Disabled Cache、開啟 Preserve Log。前後讀取 Index.html 三次。開始前先清空 Cache,因此第一次 Chrome 只能由 IIS 取回完整內容,第二次 IIS 回傳 304 通知 Chrome 使用 Cache。接著修改 Index.html 加入一個空白字元並存檔,第三次 Chrome 便會自動讀到新版。

來觀察一下這背後是如何實現「檔案沒變讀 Cache,檔案有改重新抓」?

第一次 IIS 回傳 HTTP 200,HTML 內容共 5,880 Bytes,在 Resonse Headers 有兩個 Cache 相關設定,ETag: 36b43fcfa127d31:0 是依檔案內容產生的雜湊值,Last-Modified 的時間就是 Index.html 檔案的最後修改時間(跟檔案總管查到的時間一致)。

重新整理網頁,第二次 Chrome 再送出 Request 請求時,Request Headers 多了 If-Modified-Since 及 If-None-Match,If-Modified-Since 帶入的是上圖 Last-Modified 傳回的時間,If-None-Match 帶入的則是上圖 ETag 的內容。此時 IIS 比對檔案時間及 ETag,發現檔案沒異動,傳回 HTTP 304 Not Modified,告知檔案沒變,請瀏覽器安心使用 Cache 裡的版本。不用傳回 Index.html 內容,於是省下 5,880 Bytes 的傳輸量。

接著我們修改 Index.html,在 HTML 加入一個空白字元並存檔。再次重新整理網頁,第三次 Request Headers 仍帶有相同 If-Modified-Since 及 If-None-Match,但由於 Index.html 內容有變,IIS 不再傳回 304,而是 HTTP 200 傳回完整內容(5,881 Bytes,比先前多了一個字元),而 Response Headers 中的 ETag 變成 318893c7958ed31:0 與先前不同,而 Last-Modified 也變成修改存檔時間。

由以上實驗我們可以觀察到 IIS 如何協助瀏覽器實現「平時用 Cache,有修改就重抓」,而這是所有專業網站伺服器都有的基本功能。

不過你可能發現一件事:雖然有用到 Cache,瀏覽器每次還是都要發出 Request 跟 IIS 確認檔案是否更新?可以直接用 Cache 還是重新下載?如此只能節省傳輸頻寬,並沒有減少發出 Request 的次數。

有一種更積極的 Cache 策略是 IIS 傳回結果時在 Response Header 夾帶 Cache-Control: max-age=n(多少秒),如此瀏覽器在一段時間內都不會對 IIS 發出 Request 求證檔案是否更新,直接使用 Cache 內容,可以節省更多頻寬及 CPU 資源。但副作用是萬一檔案改版,使用者可能要等內容到期後才會讀取新版內容,不然要靠在 URL 加上 ?ver=n.n.n 之類的版號參數迫使瀏覽器讀取新版。

以 IIS 10 為例,由「HTTP 回應標頭/設定一般標頭」介面可指定內容到期時間,啟用 Cache-Control: max-age 方式應用 Cache:

除了使用管理介面,Cache 政策也可透過 web.confg 設定(參考:Client Cache -clientCache- - Microsoft Docs)。

以上就是關於 IIS 靜態內容 Cache 行為的一點心得,提供大家參考。

Experiments of static file caching of IIS. Showing how If-Modified-Since, If-None-Match works.


Comments

Be the first to post a comment

Post a comment