你真的瞭解HTTP快取嗎

helloweba發表於2017-06-08

  有時,HTTP中的快取可能會非常讓人頭疼。按照文件正確地使用 HTTP 並不是那麼困難,但事實上,不同的瀏覽器和 HTTP版本常常困擾著我們。而我們自己或是不必或是沒有時間去鑽研所有的邊緣的情況。

  這裡總結的實用並速記的規則,希望對大家有所幫助

  靜態資源

  永遠不會修改的內容:JS 和 CSS 檔案,圖片,和任何型別的二進位制檔案都屬於這個類目。

  永遠,我確實說的是永遠。為靜態資源指定版本號是很通用的做法。它們無論什麼時候改動了,它們的 URL 就改變了。

  這裡是一些針對靜態資源的簡單的規則:

  在檔案或者路徑中嵌入指紋。避免為指紋使用查詢字串。另外,確保生成的URL長度超過8個不同的字元。

  使用這些 HTTP 頭:

Cache-Control: public, max-age=31536000 
Expires: (一年後的今天) 
ETag: (基於內容生成) 
Last-Modified: (過去某個時間) 
Vary: Accept-Encoding

  針對靜態資源的設定就是那麼簡單。

  動態資源

  針對應用程式私密性和新鮮度方面需求的不同,我們應該使用不同的快取控制設定。

  對於非私密性和經常性變動的資源(想像一下股票資訊),我們應該使用下面這些:

Cache-Control: public, max-age=0 
Expires: (當前時間) 
ETag: (基於內容生成) 
Last-Modified: (過去某個時間) 
Vary: Accept-Encoding

  這些設定的效果是:這些資源可以被公開地(通過瀏覽器和代理伺服器)快取起來。每一次在瀏覽器使用這些資源之前,瀏覽器或者代理伺服器會檢查這些資源是否有更新的版本,如果有,就把它們下載下來。

  這樣的設定需要注意,瀏覽器在重新檢查資源時效性方面有一定的靈活性。典型的是,當使用者點選了「返回/前進」按鈕時,瀏覽器不會重新檢查這些資原始檔,而是直接使用快取的版本。你如果需要更嚴格的控制,需要告知瀏覽器即使當使用者點選了「返回/前進」按鈕,也需要重新檢查這些資原始檔,那麼可以使用:

Cache-Control: public, no-cache, no-store

  不是所有的動態資源都會馬上變成過時的資源。如果它們可以保持至少5分鐘的時效,可以使用:

Cache-Control: public, max-age=300

  經過這樣的設定,瀏覽器只會在5分鐘之後才重新檢查。在這之前,快取的內容會被直接使用。如果在5分鐘後,這些過時的內容需要嚴格控制,你可以新增must-revalidate欄位:

Cache-Control: public, max-age=300, must-revalidate

  對於私密或者針對使用者的內容,需要把 public 替換為 private 以避免內容被代理快取。

Cache-Control: private, …

  Cache-Control 和 Expires

  當同時使用 Cache-Control 和 Expires 時,Cache-Control 獲得優先權。

  同時使用 Cache-Control 和 Expires 意味著得到更廣泛的支援(被不同的瀏覽器和版本)。當然,它們兩個應該被配置成相同的時效值,以避免引起困惑。

  參考 Expires: vs. Cache-Control: max-age

  ETag 和 Last-Modified

  這兩個頭在瀏覽器對資源做重新檢查驗證的時候會使用到。大致來說,瀏覽器只是盲目地儲存這兩個來自於伺服器的頭的值,然後在需要檢查驗證的時候,瀏覽器根據請求條件,把這兩個指傳送給伺服器(分別通過 If-None-Match 和 If-Modified-Since)。

  注意只有在資源過期的情況下,檢查驗證才會發生。

  在有條件的請求下,If-None-Match 和 If-Modified-Since 頭的出現取決於伺服器。然而,由於是伺服器生成的 ETag 和(或) Last-Modified,所以實際上,這沒有什麼大問題。大多數的瀏覽器在可能的情況下都會把著兩者都傳送給伺服器。

  參考 What takes precedence: the ETag or Last-Modified HTTP header?

一個通常的建議是:避免使用 ETag。這不是一個總是有用的建議。ETag 在判斷內容是否真的改動方面確實提供了更為精確的控制。針對生成的 ETag,預設的Apache方法需要把檔案的索引節(inode),大小(size)和最後修改時間作為輸入求值得到。這會導致在負載均衡的環境中,生成的 ETag 值變得毫無用處,因為每個伺服器都會針對相同的檔案生成一個不同的 Etag 值。這個可能就是唯一的問題導致很多人完全禁用 ETag,其實只要精確地針對一個匹配的檔案生成一個獨一無二的 ETag 值,就沒有必要禁用 ETag 了。

  手動按下 Ctrl-R

  當按下 Ctrl-R 時,瀏覽器會攜帶下面的請求,以檢查是否需要更新快取內容:

Cache-Control: max-age=0 
If-None-Match: … 
If-Modifed-Since: …

  注意這並不只是和原伺服器建立連線,其同樣適用於代理伺服器。本質上,它只是重新檢查驗證內容。如果伺服器回應了一個304,瀏覽器將會使用快取的內容。

  Vary: Accept-Encoding

  這個頭對於一些人來說可能比較陌生。

  當一個資源啟用了 gzip 壓縮,並且被代理伺服器快取,客戶端如果不支援 gzip 壓縮,那麼在這樣的情況下將會得到不正確的資料(也就是,壓縮過的資料)。這將會使代理伺服器快取兩個版本的資源:一個是壓縮過的,一個是沒壓縮過的。正確版本的資源將在請求頭髮送之後進行傳輸。

  還有一個現實的原因:IE 瀏覽器不快取任何帶有 Vary 頭但值不為 Accept-Encoding 和 User-Agent 的資源。所以通過這種方式新增這個頭,才能確保這些資源在 IE 下被快取。

  本文譯自 Bryan Tsai 的 《Http Caching》。

相關文章