[廚餘回收] 為何 IIS 沒有刷新我的靜態檔案?

  • 5356
  • 0
  • IIS
  • 2018-11-25

這是個對 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 問題就解決了,現在知道了更進一步的問題原因,就不會只會施展一種解法。

參考資料

相關資源

C# 指南
ASP.NET 教學
ASP.NET MVC 指引
Azure SQL Database 教學
SQL Server 教學
Xamarin.Forms 教學