這是個對 IIS 的誤會,這個跟各個瀏覽器如何處理 Cache-Control 有關,IIS 在靜態檔案上預設沒有回應 Cache-Control 這個標頭,我比較了三款瀏覽器 Chrome(70.0.3538.77)、Firefox(63.0)、Edge(44.17763.1.0)來看看它們各自如何處理這種情況?以及 IIS 該如何來調整?
假定我有一個 test.cshtml
,裡面引用了 /js/test.js
檔案,會在畫面上印出 test
字樣。
沒有 Cache-Control 回應標頭
從 Fiddler 看 /js/test.js 的回應內容,沒有看見有 Cache-Control 標頭,雖然沒有 Cache-Control 標頭,但是 IIS 有給 Last-Modified 及 ETag 標頭。
首次發送資源請求時,三款瀏覽器都是一樣的,都需要從伺服器下載資源內容,那我們來看看從第二次開始會有什麼不同?
Chrome
首先是 Chrome,當我重新整理頁面之後,回應是 200 但是是 from memory cache
,表示 Chrome 並沒有發送請求,而是直接從它的 memory cache 中取得資源內容。
Firefox
再來是 Firefox,重新整理頁面之後,回應是 304(Not Modified),表示 Firefox 有向伺服器發送請求。
Edge
最後是 Edge,重新整理頁面之後,回應是 304(Not Modified),跟 Firefox 一樣也向伺服器發送了請求。
經過剛剛的測試,Chrome 跟另外兩款瀏覽器的結果不一樣,當沒有 Cache-Control 回應標頭時,它自己有一套 Cache 機制,這就造成了一個問題,我伺服器的靜態檔案更新了,但是不知道 Chrome 何時才會發送請求來跟伺服器要新的資源內容?(似乎是以靜態檔案的 Last-Modified 來計算間隔多久後向伺服器發送請求,我不確定,有朋友知道的話麻煩指點一二。)
回應 Cache-Control 標頭
面對 Chrome 瀏覽器這樣的情形,只要回應 Cache-Control 標頭,並且明確地告知快取的策略,它就會乖乖的,畢竟 Chrome 身為主流的瀏覽器之一,還是會照著 RFC 的標準在走。
因此我們就回頭從 IIS 下手,讓靜態檔案的回應標頭加上 Cache-Control: max-age=[n]
就可以了,加的方式有兩種,第一種是從 IIS 管理員去設定,但我 prefer 第二種方式,在 Web.Config 設定 <system.webServer>/<staticContent>
的 <clientCache>
。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
...
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="00:00:05" />
</staticContent>
</system.webServer>
</configuration>
如果要針對特定路徑去設定的話就用 <location>
,path
指的是相對路徑,可以是某個檔案 js/test.js、某個目錄 js、某個子目錄 js/sub,另外 cacheControlMaxAge
如果要設成一天的話,其值為 1.00:00:00
。。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
...
<location path="js">
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="00:00:05" />
</staticContent>
</system.webServer>
</location>
...
</configuration>
調整完後,當設定的 Cache 到期時,Chrome 就會來伺服器問,資源內容沒有變的話伺服器就回應 304(Not Modified)。
如果資源內容有更新,伺服器就會回應 200 並且把新的資源內容傳給 Chrome。
以前遇到這種態靜檔案沒有更新的問題,就會幫靜態檔案的路徑隨意多加個 QueryString 問題就解決了,現在知道了更進一步的問題原因,就不會只會施展一種解法。