瀏覽器快取機制詳解

風靈使發表於2018-06-03

對於瀏覽器快取,相信很多開發者對它真的是又愛又恨。一方面極大地提升了使用者體驗,而另一方面有時會因為讀取了快取而展示了“錯誤”的東西,而在開發過程中千方百計地想把快取禁掉。那麼瀏覽器快取究竟是個什麼樣的神奇玩意呢?

什麼是瀏覽器快取:

簡單來說,瀏覽器快取就是把一個已經請求過的Web資源(如html頁面,圖片,js,資料等)拷貝一份副本儲存在瀏覽器中。快取會根據進來的請求儲存輸出內容的副本。當下一個請求來到的時候,如果是相同的URL,快取會根據快取機制決定是直接使用副本響應訪問請求,還是向源伺服器再次傳送請求。比較常見的就是瀏覽器會快取訪問過網站的網頁,當再次訪問這個URL地址的時候,如果網頁沒有更新,就不會再次下載網頁,而是直接使用本地快取的網頁。只有當網站明確標識資源已經更新,瀏覽器才會再次下載網頁。至於瀏覽器和網站伺服器是如何標識網站頁面是否更新的機制,將在後面介紹。
這裡寫圖片描述

上圖就是使用了快取的栗子,在頁面請求之後,web資源都被快取了,在後面的重複請求中,可以看到許多資源都是直接從快取中讀取的(from cache),而不是重新去向伺服器請求。

為什麼使用快取:

(1)減少網路頻寬消耗

無論對於網站運營者或者使用者,頻寬都代表著金錢,過多的頻寬消耗,只會便宜了網路運營商。當Web快取副本被使用時,只會產生極小的網路流量,可以有效的降低運營成本。

(2)降低伺服器壓力

給網路資源設定有效期之後,使用者可以重複使用本地的快取,減少對源伺服器的請求,間接降低伺服器的壓力。同時,搜尋引擎的爬蟲機器人也能根據過期機制降低爬取的頻率,也能有效降低伺服器的壓力。

(3)減少網路延遲,加快頁面開啟速度

頻寬對於個人網站運營者來說是十分重要,而對於大型的網際網路公司來說,可能有時因為錢多而真的不在乎。那Web快取還有作用嗎?答案是肯定的,對於終端使用者,快取的使用能夠明顯加快頁面開啟速度,達到更好的體驗。

瀏覽器端的快取規則:

對於瀏覽器端的快取來講,這些規則是在HTTP協議頭和HTML頁面的Meta標籤中定義的。他們分別從新鮮度和校驗值兩個維度來規定瀏覽器是否可以直接使用快取中的副本,還是需要去源伺服器獲取更新的版本。

新鮮度(過期機制):也就是快取副本有效期。一個快取副本必須滿足以下條件,瀏覽器會認為它是有效的,足夠新的:

  1. 含有完整的過期時間控制頭資訊(HTTP協議報頭),並且仍在有效期內;
  2. 瀏覽器已經使用過這個快取副本,並且在一個會話中已經檢查過新鮮度;

滿足以上兩個情況的一種,瀏覽器會直接從快取中獲取副本並渲染。

校驗值(驗證機制):伺服器返回資源的時候有時在控制頭資訊帶上這個資源的實體標籤EtagEntity Tag),它可以用來作為瀏覽器再次請求過程的校驗標識。如過發現校驗標識不匹配,說明資源已經被修改或過期,瀏覽器需求重新獲取資源內容。

瀏覽器快取的控制:

(1)使用HTML Meta 標籤

Web開發者可以在HTML頁面的<head>節點中加入<meta>標籤,程式碼如下

<meta http-equiv="Pragma" content="no-cache">  
<!- Pragmahttp1.0版本中給客戶端設定快取方式之一,具體作用會在後面詳細介紹 -->

上述程式碼的作用是告訴瀏覽器當前頁面不被快取,每次訪問都需要去伺服器拉取。但是!這裡有個坑…

事實上這種禁用快取的形式用處很有限:

a. 僅有IE才能識別這段meta標籤含義,其它主流瀏覽器僅識別“Cache-Control: no-store”的meta標籤。

b. 在IE中識別到該meta標籤含義,並不一定會在請求欄位加上Pragma,但的確會讓當前頁面每次都發新請求(僅限頁面,頁面上的資源則不受影響)。

(2)使用快取有關的HTTP訊息報頭

在這裡就需要先跟大家介紹一下HTTP的相關知識。一個URI的完整HTTP協議互動過程是由HTTP請求和HTTP響應組成的。有關HTTP詳細內容可參考《Hypertext Transfer Protocol — HTTP/1.1》《HTTP協議詳解》等。

在HTTP請求和響應的訊息報頭中,常見的與快取有關的訊息報頭有:

規則 訊息包頭 值/示例 型別 作用
新鮮度 Pragma no-cache 響應 告訴瀏覽器忽略資源的快取副本,每次訪問都需要去伺服器拉取【http1.0中存在的欄位,在http1.1已被拋棄,使用Cache-Control替代,但為了做http協議的向下相容,很多網站依舊會帶上這個欄位】
Expires Mon, 15 Aug 2016 03:56:47 GMT 響應 啟用快取和定義快取時間。告訴瀏覽器資源快取過期時間,如果還沒過該時間點則不發請求【http1.0中存在的欄位,該欄位所定義的快取時間是相對伺服器上的時間而言的,如果客戶端上的時間跟伺服器上的時間不一致(特別是使用者修改了自己電腦的系統時間),那快取時間可能就沒啥意義了。在HTTP 1.1版開始,使用Cache-Control: max-age=秒替代】
Cache-Control no-cache 響應 告訴瀏覽器忽略資源的快取副本,強制每次請求直接傳送給伺服器,拉取資源,但不是“不快取”
no-store 響應 強制快取在任何情況下都不要保留任何副本
max-age=[秒] 響應 指明快取副本的有效時長,從請求時間開始到過期時間之間的秒數
public 響應 任何路徑的快取者(本地快取、代理伺服器),可以無條件的快取改資源
private 響應 只針對單個使用者或者實體(不同使用者、視窗)快取資源
Last-Modified Mon, 15 Aug 2016 03:56:47 GMT 響應 告訴瀏覽器這個資源最後的修改時間。伺服器將資源傳遞給客戶端時,會將資源最後更改的時間以“Last-Modified: GMT”的形式加在實體首部上一起返回給客戶端【只能精確到秒級,如果某些檔案在1秒鐘以內,被修改多次的話,它將不能準確標註檔案的修改時間】
If-Modified-Since Mon, 15 Aug 2016 03:56:47 GMT 請求 其值為上次響應頭的Last-Modified值,再次向web伺服器請求時帶上頭If-Modified-Since。web伺服器收到請求後發現有頭If-Modified-Since則與被請求資源的最後修改時間進行比對。若最後修改時間較新,說明資源又被改動過,則響應整片資源內容(寫在響應訊息包體內),包括更新Last-Modified的值,HTTP 200;若最後修改時間較舊,說明資源無新修改,則響應HTTP 304(無需包體,節省瀏覽),告知瀏覽器繼續使用所儲存的cache
校驗值 ETag “fd56273325a2114818df4f29a628226d” 響應 告訴瀏覽器當前資源在伺服器的唯一識別符號(生成規則又伺服器決定)
If-None-Match “fd56273325a2114818df4f29a628226d” 請求 當資源過期時(使用Cache-Control標識的max-age),發現資源具有Etage宣告,則再次向web伺服器請求時帶上頭If-None-Match(Etag的值)。web伺服器收到請求後發現有頭If-None-Match則與被請求資源的相應校驗串進行比對,決定返回200或304

在我們對HTTP請求頭和響應頭的部分欄位有了一定的認識之後,我們接下來就來討論不同欄位之間的關係和區別:

Cache-Control與Expires

Cache-ControlExpires的作用一致,都是指明當前資源的有效期,控制瀏覽器是否直接從瀏覽器快取取資料還是重新發請求到伺服器取資料。只不過Cache-Control的選擇更多,設定更細緻,如果同時設定的話,其優先順序高於Expires

Last-Modified/ETag與Cache-Control/Expires

配置Last-Modified/ETag的情況下,瀏覽器再次訪問統一URI的資源,還是會傳送請求到伺服器詢問檔案是否已經修改,如果沒有,伺服器會只傳送一個304回給瀏覽器,告訴瀏覽器直接從自己本地的快取取資料;如果修改過那就整個資料重新發給瀏覽器;

Cache-Control/Expires則不同,如果檢測到本地的快取還是有效的時間範圍內,瀏覽器直接使用本地副本,不會傳送任何請求。兩者一起使用時,Cache-Control/Expires的優先順序要高於Last-Modified/ETag。即當本地副本根據Cache-Control/Expires發現還在有效期內時,則不會再次傳送請求去伺服器詢問修改時間(Last-Modified)或實體標識(Etag)了。

一般情況下,使用Cache-Control/Expires會配合Last-Modified/ETag一起使用,因為即使伺服器設定快取時間, 當使用者點選“重新整理”按鈕時,瀏覽器會忽略快取繼續向伺服器傳送請求,這時Last-Modified/ETag將能夠很好利用304,從而減少響應開銷。

Last-Modified與ETag

你可能會覺得使用Last-Modified已經足以讓瀏覽器知道本地的快取副本是否足夠新,為什麼還需要Etag(實體標識)呢?HTTP1.1Etag的出現主要是為了解決幾個Last-Modified比較難解決的問題:

  • Last-Modified標註的最後修改只能精確到秒級,如果某些檔案在1秒鐘以內,被修改多次的話,它將不能準確標註檔案的新鮮度
  • 如果某些檔案會被定期生成,當有時內容並沒有任何變化,但Last-Modified卻改變了,導致檔案沒法使用快取
  • 有可能存在伺服器沒有準確獲取檔案修改時間,或者與代理伺服器時間不一致等情形

Etag是伺服器自動生成或者由開發者生成的對應資源在伺服器端的唯一識別符號,能夠更加準確的控制快取。Last-ModifiedETag是可以一起使用的,伺服器會優先驗證ETag,一致的情況下,才會繼續比對Last-Modified,最後才決定是否返回304。Etag的伺服器生成規則和強弱Etag的相關內容可以參考,《互動百科-Etag》《HTTP Header definition》,這裡不再深入。

注意:

  1. Etag是伺服器自動生成或者由開發者生成的對應資源在伺服器端的唯一識別符號,能夠更加準確的控制快取,但是需要注意的是分散式系統裡多臺機器間檔案的last-modified必須保持一致,以免負載均衡到不同機器導致比對失敗,Yahoo建議分散式系統儘量關閉掉Etag(每臺機器生成的etag都會不一樣,因為除了 last-modified、inode 也很難保持一致)。

  2. Last-Modified/If-Modified-Since要配合Cache-Control使用,Etag/If-None-Match也要配合Cache-Control使用。

瀏覽器HTTP請求流程:

第一次請求:
這裡寫圖片描述
再次請求:
這裡寫圖片描述

使用者行為與快取:

瀏覽器快取行為還有使用者的行為有關,具體情況如下:

使用者操作 Expires/Cache-Control Last-Modified/Etag
位址列回車 有效 有效
頁面連結跳轉 有效 有效
新開視窗 有效 有效
前進、後退 有效 有效
F5重新整理 無效(BR重置max-age=0) 有效
Ctrl+F5重新整理 無效(重置Cache-Control=no-cache) 無效(請求頭丟棄該選項

不能快取的請求:

當然並不是所有請求都能被快取,無法被瀏覽器快取的請求如下:

  1. HTTP資訊頭中包含Cache-Control:no-cachepragma:no-cache(HTTP1.0),或Cache-Control:max-age=0等告訴瀏覽器不用快取的請求

  2. 需要根據Cookie,認證資訊等決定輸入內容的動態請求是不能被快取的

  3. 經過HTTPS安全加密的請求(有人也經過測試發現,ie其實在頭部加入Cache-Control:max-age資訊,firefox在頭部加入Cache-Control:Public之後,能夠對HTTPS的資源進行快取,參考《HTTPS的七個誤解》

  4. POST請求無法被快取

  5. HTTP響應頭中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的請求無法被快取

相關文章