瀏覽器緩存機製
分類
瀏覽器的緩存主要包括兩種緩存:強緩存、驗證緩存。
強緩存是指瀏覽器不與服務器進行任何交互請求,直接將瀏覽器的緩存數據(包括緩存數據的response頭信息)返回給用戶。這種緩存給用戶的響應是最快的,但同時也是風險性較高的。因為該類緩存沒有進行任何的校驗即直接反饋給用戶,是可能存在有曆史的髒數據。可以從以下兩個方麵查看到該次請求是強緩存:
1)瀏覽器返回200 (From Cache):
2)Response頭中的Date字段所表示的時間小於當前時間(Date字段表示的是源站服務器第一次產生響應數據的時間):
強緩存所依賴的header頭包括:Cache-Control:max-age和Expires頭。Cache-Control頭和Expires頭都是都是緩存數據的有效期的信息。隻不過HTTP/1.0+的Expires頭是采用的絕對GMT時間,而HTTP/1.1的Cache-Control:max-age則是采用的相對時間進行存儲。在實際使用中我們更傾向於使用Cache-Control:max-age頭,因為Expires頭記錄的是服務器端設置的絕對時間,如果客戶端與服務器之間的時間差別較大的話可能會導致有偏差;並且當Cache-Control:max-age和Expires頭同時存在的情況下,Cache-Control頭將覆蓋Expires頭。當請求發起的時間仍然在Cache-Control或者Expires設置的有效期內的話則將直接讀取緩存數據。
驗證緩存(又叫協商緩存)是指瀏覽器根據緩存資源的Last-Modified字段和Etag字段得到If-Modified-Since和If-None-Match字段加入request頭中向服務器進行驗證源站服務器的資源是否有更新過,如果發現源站資源沒有進行變更即會返回304 Not Modified響應頭。
下麵分別介紹這幾個字段的意義:
Last-Modified/If-Modified-Since:在客戶端第一次向服務器端發起請求時,服務器返回數據並置狀態碼為200,同時將該文件最後修改的GMT時間記錄在Last-Modified頭中返回客戶端。下次客戶端請求驗證緩存數據時就會將緩存數據中的Last-Modified字段記錄為請求頭中的If-Modified-Since字段向服務器端詢問在該時間點後服務器的文件是否有做過更新,如果沒有更新即返回304 Not Modified響應頭並讀取緩存數據,而如果服務器文件該時間點後更新過則需要重新將服務器的文件傳輸給客戶端並返回200狀態碼,同時該文件的Last-Modified時間也將是服務器文件現在更新的時間。
Etag/If-None-Match:HTTP協議規格說明定義ETag為“被請求變量的實體值”。另一種說法是,ETag是一個可以與Web資源關聯的記號(token)。HTTP/1.1並沒有要求具體Etag中間需要存放什麼內容或者實現方法,有一些ETag是通過文件資源的MD5值來進行標識的。與Last-Modified一樣也是判斷服務器文件是否有做過更新,其也是將上次緩存數據中的Etag記錄為請求頭中的If-None-Match頭向服務器驗證服務器文件的Etag是否更新過。有Etag驗證主要解決以下幾點Last-Modified無法解決的問題:
1、網站文件周期性更新但並不改變文件內容(僅修改Last-Modified時間),對於這些文件仍然希望可以使用緩存數據;
2、網站文件更新頻率較快,小於秒級的更新頻率通過Last-Modified無法識別;
3、服務器無法準確得到Last-Modified時間,需要Etag標識文件。
瀏覽器行為
瀏覽器有多種刷新行為可以影響下次請求對緩存數據的行為,並且不同的瀏覽器對於同樣的刷新操作也有不同的情況。但是究其原因都是瀏覽器在發起請求的時候所帶的catch-control的頭信息來決定的。下麵是HTTP/1.1文檔中13.2.6 Disambiguating Multiple Responses的一段文檔[1]:
When a client tries to revalidate a cache entry, and the response it receives contains a Date header that appears to be older than the one for the existing entry, then the client SHOULD repeat the request unconditionally, and include
Cache-Control: max-age=0
to force any intermediate caches to validate their copies directly with the origin server, or
Cache-Control: no-cache
to force any intermediate caches to obtain a new copy from the origin server.
從上麵該文檔可以知曉:如果一個client想忽略強緩存的數據而直接獲取驗證緩存的數據的話是需要在請求頭中加上Cache-Control:max-age=0;而client如果想忽略強緩存和鑒權緩存,直接獲取源服務器的內容而緩存數據全部重新從源站獲取新的數據就需要在請求頭中加上Cache-Control:no-cache的頭。下麵是在chrome下測試的結果圖:
上麵的測試分別是通過在地址欄回車重新鍵入地址、F5刷新以及Ctrl+F5刷新的測試結果。從上麵的結果圖中驗證了上麵的結論,並且其中對於Cache-Control: no-cache的測試中為了兼容HTTP/1.0加上了Pragma:no-cache的頭信息,其作用於前者是一致的。因此不同的瀏覽器對於不同的刷新操作有不同的處理邏輯也是由request頭中的Cache-Control頭決定的。
流程總結
下麵將根據兩張圖[2][3]總結瀏覽器緩存的處理邏輯。
(1)當瀏覽器向服務器端發送請求的時候首先查看查看本地瀏覽器緩存數據中是否有緩存數據,如果沒有緩存數據則會向web服務器(這裏的web服務器是廣義的概念,有可能並不是源站服務器,有可能是CDN等緩存數據)請求對應的數據並將獲取的響應數據以及一些對應的response頭信息緩存到本地(包括Expires、Cache-Control等頭信息),如果有緩存數據則執行(2);
(2)當本地有緩存數據並且request頭中沒有設置Cache-Control:no-cache和Cache-Control:max-age=0頭信息的話就需要查看該緩存的Cache-Control頭和Expires頭查看該緩存數據是否新鮮,如果沒有過期則直接讀取強緩存數據返回給瀏覽器,返回狀態碼200(from cache)。如果有設置上述的兩個Cache-Control頭或者強緩存數據已經過期則執行(3);
(3)如果請求頭中沒有Cache-Control:no-cache頭信息的話則客戶端帶著If-Modified-Since和If-None-Match參數向服務器發起驗證,如果服務器端驗證發現沒有進行更新的話則直接返回304 Not Modified頭和本地的緩存數據返回給客戶端。如果請求頭帶了Cache-Control:no-cache或者源站做了更新則執行(4);
(4)如果請求頭中有Cache-Control:no-cache或者驗證緩存校驗發現源站數據更新了,則需要從服務器重新獲取數據並將其存入瀏覽器緩存並返回200狀態碼。
Reference
[1] Hypertext Transfer Protocol -- HTTP/1.1: https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
[2] HTTP 權威指南
[3] 緩存係列(1)——瀏覽器緩存協商: https://blog.csdn.net/chosen0ne/article/details/7344189
[4] http協商緩存VS強緩存: https://www.cnblogs.com/wonyun/p/5524617.html
[5] stackoverflow討論帖: https://stackoverflow.com/questions/1046966/whats-the-difference-between-cache-control-max-age-0-and-no-cache
最後更新:2017-06-16 17:01:45