瀏覽器一般緩存圖片、CSS、JS等靜態文件,因為這些文件的更新頻率相對來說比較低,合理利用瀏覽器的緩存對網站的性能提升有很大幫助。HTTP緩存分為兩部分,分別是本地緩存和緩存協商,當本地緩存不生效時會啟用緩存協商。HTTP緩存主要由HTTP協議的頭(Header)信息來制定。
本地緩存
本地緩存是指當瀏覽器請求資源時,如果命中了瀏覽器本地的緩存資源,那么瀏覽器就不會發送真正請求給服務器。它的執行過程是:
- 第一次瀏覽器發送請求給服務器時,此時瀏覽器還沒有本地緩存副本,服務器返回資源給瀏覽器,響應碼是200 OK,瀏覽器收到資源后,把資源和對應的響應頭一起緩存下來。 2. 第二次瀏覽器準備發送請求給服務器時候,瀏覽器會先檢查上一次服務端返回的響應頭信息中的Cache-Control(它的值是一個相對值,單位為秒,表示資源在客戶端緩存的最大有效期),過期時間為第一次請求的時間加上Cache-Control的值,過期時間跟當前的請求時間比較,如果本地緩存資源沒過期,那么命中緩存,不再請求服務器。 3. 如果沒有命中,瀏覽器就會把請求發送給服務器,進入緩存協商階段。
與本地緩存相關的頭有:Cache-Control、Expires,Cache-Control有多個可選值代表不同的意義,而Expires就是一個日期格式的絕對值。
Cache-Control
Cache-Control是HTTP緩存策略中最重要的頭信息,它是HTTP/1.1中出現的,它由如下幾個值 no-cache:不使用本地緩存。需要使用緩存協商,先與服務器確認返回的響應是否被更改,如果之前的響應中存在ETag,那么請求的時候會與服務端驗證,如果資源未被更改,則可以避免重新下載。 no-store:直接禁止游覽器緩存數據,每次用戶請求該資源,都會向服務器發送一個請求,每次都會下載完整的資源。 public:可以被所有的用戶緩存,包括終端用戶和CDN等中間代理服務器。 private:只能被終端用戶的瀏覽器緩存,不允許CDN等中繼緩存服務器對其緩存。 * max-age:從當前請求開始,允許獲取的響應被重用的最長時間(秒)。
例如:Cache-Control:public, max-age=1000
表示資源可以被所有用戶以及代理服務器緩存,最長時間為1000秒。
Expires
Expires是HTTP/1.0出現的頭信息,同樣是用于決定本地緩存策略的頭,它是一個絕對時間,時間格式是如Mon, 10 Jun 2015 21:31:12 GMT,只要發送請求時間是在Expires之前,那么本地緩存始終有效,否則就會去服務器發送請求獲取新的資源。如果同時出現Cache-Control:max-age和Expires,那么max-age優先級更高。他們可以這樣組合使用:
Cache-Control:public
Expires: Mon, 10 Jun 2015 21:31:12 GMT
緩存協商
當第一次請求時服務器返回的響應頭中沒有Cache-Control和Expires或者Cache-Control和Expires過期抑或它的屬性設置為no-cache時,那么瀏覽器第二次請求時就會與服務器進行協商,詢問瀏覽器中的緩存資源是不是舊版本,需不需要更新,此時,服務器就會做出判斷,如果緩存和服務端資源的最新版本是一致的,那么就無需再次下載該資源,服務端直接返回304 Not Modified 狀態碼,如果服務器發現瀏覽器中的緩存已經是舊版本了,那么服務器就會把最新資源的完整內容返回給瀏覽器,狀態碼就是200 Ok,那么服務端是根據什么來判斷瀏覽器的緩存是不是最新的呢?其實是根據HTTP的另外兩組頭信息,分別是:Last-Modified/If-Modified-Since 與 ETag/If-None-Match。
Last-Modified與If-Modified-Since
瀏覽器第一次請求資源時,服務器會把資源的最新修改時間Last-Modified:Thu, 29 Dec 2011 18:23:55 GMT放在響應頭中返回給瀏覽器,第二次請求時,瀏覽器就會把上一次服務器返回的修改時間放在請求頭If-Modified-Since:Thu, 29 Dec 2011 18:23:55發送給服務器,服務器就會拿這個時間跟服務器上的資源的最新修改時間進行對比,如果兩者相等或者大于服務器上的最新修改時間,那么表示瀏覽器的緩存是有效的,此時緩存會命中,服務器就不再返回內容給瀏覽器了,同時Last-Modified頭也不會返回,因為資源沒被修改,返回了也沒什么意義。如果沒命中緩存則最新修改的資源連同Last-Modified頭一起返回。
第一次請求返回的響應頭:
Cache-Control:max-age=3600
Expires:Tue, 26 Jan 2016 08:28:52 GMT
Last-Modified:Fri, 15 Jan 2016 12:06:06 GMT
第二次請求返回的相應頭
If-Modified-Since:Fri, 15 Jan 2016 12:06:06 GMT
這組頭信息是基于資源的修改時間來判斷資源有沒有更新,另一種方式就是根據資源的內容來判斷,就是接下來要討論的ETag與If-None-Match
ETag與If-None-Match
ETag/If-None-Match與Last-Modified/If-Modified-Since的流程其實是類似的,唯一的區別是它基于資源的內容的摘要信息(比如MD5 hash)來判斷。瀏覽器發送第二次請求時,會把第一次的響應頭信息ETag的值放在If-None-Match的請求頭中發送到服務器,與最新的資源的摘要信息對比,如果相等,取瀏覽器緩存,否則內容有更新,最新的資源連同最新的摘要信息返回。用ETag的好處是如果因為某種原因到時資源的修改時間沒改變,那么用ETag就能區分資源是不是有被更新。
第一次請求返回的響應頭:
Cache-Control:public, max-age=31536000
ETag: "15f0fff99ed5aae4edffdd6496d7131f"
第二次請求的請求頭信息:
If-None-Match: "15f0fff99ed5aae4edffdd6496d7131f"