HTTP快取策略學習總結
所有的快取都是基於一套規則來幫助他們決定什麼時候使用快取中的副本提供服務(假設有副本可用的情況下,未被銷燬回收或者未被刪除修改)。這些規則有的在協議中有定義(如HTTP1.0和HTTP1.1),有的則是由快取的管理員設定(如DBA、瀏覽器的使用者、代理伺服器管理員或者應用開發者)
瀏覽器的快取規則
對於瀏覽器端的快取來講,這些規則是在HTTP協議頭和HTML頁面的Meta標籤中定義的。他們分別從新鮮度和校驗值兩個維度來規定瀏覽器是否可以直接使用快取中的副本,還是需要去原伺服器獲取更新的版本。
新鮮度(過期機制):也就是快取副本有效期
校驗值(驗證機制):伺服器返回資源的時候有時在控制頭資訊帶上這個資源的實體標籤eTag,它可以用來作為再次請求過程的校驗標識。如果發現校驗標識不匹配,說明資源已經被修改或過期,瀏覽器需求重新獲取資源內容。
瀏覽器快取的控制
使用快取有關的HTTP訊息報頭
規則 | 訊息報頭 | 值/示例 | 型別 | 作用 |
新鮮度 | Expires | Sun,16 Oct 2016 05:43:02 GMT | 響應 | 告訴瀏覽器在過期時間前可以使用副本(有可能存在時間不一致問題) |
Pragma | no-cache | 響應 | 告訴瀏覽器忽略資源的快取副本(HTTP1.1可用Cache-Control替換) | |
Cache-Control | no-cache | 響應 | 告訴瀏覽器忽略資源的快取副本(HTTP1.1可用Cache-Control替換) | |
no-store | 響應 | 強制快取在任何情況下都不要保留任何副本 | ||
max-age=[秒] | 響應 | 指明快取副本的有效時長,從請求時間開始到過期時間之間的秒數 | ||
public | 響應 | 任何途徑的快取者(本地快取、代理伺服器),可以無條件的快取該資源 | ||
private | 響應 | 只針對單個使用者或實體(不同使用者視窗)快取資源 | ||
Last-Midified | Sun,16 Oct 2016 05:43:02 GMT | 響應 | 告訴瀏覽器當前資源的最後修改時間 | |
If-Modified-Since | Sun,16 Oct 2016 05:43:02 GMT | 請求 | 如果瀏覽器第一次請求時響應中Last-Modified非空,第二次請求同一資源時,會把它作為該值發給伺服器 | |
校驗值 | ETag | 檔案的索引節(INode),大小(Size)和最後修改時間(MTime)的Hash | 響應 | 告知瀏覽器當前資源在伺服器的唯一識別符號(生成規則有伺服器決定) |
If-None-Match | 檔案的索引節(INode),大小(Size)和最後修改時間(MTime)的Hash | 請求 | 如果瀏覽器第一次請求響應中ETag非空,第二次請求同一資源時,會把他作為該項的值發給伺服器 | |
輔助 | Vary | Accept-Encoding | 響應 | 輔助從多個快取副本中篩選出合適的版本 |
這裡解釋一下Cache-Control與Expires
Cache-Control與Expires的作用一致,都是指明當前資源的有效期,如果在有效期內,瀏覽器直接讀取快取中的資料並返回200 from cache,不需要傳送請求到伺服器。如果不在有效期內,則傳送請求。只不過Cache-Control的選擇更多,設定更細緻,如果同時設定的話,其優先順序高於Expires。
Last-Modified與ETage
伺服器將資源傳遞給客戶端時,會將資源最後更改的事件以Last-Modified:GMT的形式載入實體首部上一起返回給客戶端。客戶端會為資源標記上該資訊,下次再次請求時,會把該資訊附帶在請求頭(‘If-Modified-Since’)一併帶給伺服器去做檢查,若傳輸的時間值與伺服器上該資源最終修改時間是一致的,則說明該資源沒有被修改過,直接返回304狀態碼即可。
Last-Modified有一個缺點,如果在伺服器上,一個資源被修改了,但是實際內容根本沒有發生改變,會因為Last-Modified時間匹配不上而返回了整個例項給客戶端(即使客戶端快取裡有個一模一樣的資源)。
為了解決上述Last-modified可能存在的不準確的問題,HTTP1.1推出了ETag實體首部欄位
伺服器會通過某種演算法,給資源計算得出一個唯一識別符號,在把資源響應給客戶端的時候,會在實體首部加上ETag一起返回給客戶端
客戶端會保留該ETag欄位,並在下一次請求時將其一併帶過去給伺服器(請求頭If-None-Match)。伺服器只需要比較客戶端傳來的ETag跟自己伺服器上資源的ETag是否一致,就能很好地判斷資源相對客戶端而言是否被修改過,如果伺服器發現ETag匹配不上,那麼直接以常規GET200回包形式將新的資源(包括新的ETag)發給客戶端;如果ETag是一致的,則直接返回304知會客戶端直接使用本地快取即可。
Last-Modified/ETag與Cache-Control/Expires
配置Last-Modified/ETag的情況下,瀏覽器再次訪問資源的時候,還是會發送請求到伺服器詢問是否已經修改,如果沒有,伺服器傳送304狀態碼給瀏覽器,告訴瀏覽器直接從自己本地快取中讀取資料,如果修改過那就整個資料重新發給瀏覽器
Cache-Control/Expires則不同,如果檢測到本地的快取還是有效的事件範圍內,瀏覽器直接使用本地副本,不會發送任何請求。兩者一起使用時,Cache-Control/Expires的優先順序要高於Last-Modified/ETag。即當本地副本根據Cache-Control/Expires發現還在有效期內時,則不會再發送請求去伺服器詢問修改時間(Last-Modified)或實體標識(ETag)了。所以,當一些資源有可能改動比較頻繁時,設定過期時間要短,不然會發生伺服器端更新了資源,瀏覽器因為讀的是快取而不能載入最新資源。
使用者操作行為與快取
使用者在使用瀏覽器的時候,會有各種操作,比如輸入地址後回車,按F5重新整理等,這些行為會對快取有一定的影響
使用者操作 | Cache-Control/Expires | Last-Modified/ETag |
位址列回車 | 有效 | 有效 |
頁面連結跳轉 | 有效 | 有效 |
新開視窗 | 有效 | 有效 |
前進後退 | 有效 | 有效 |
F5重新整理 | 無效 | 有效 |
ctrl+f5強制重新整理 | 無效 | 無效 |
通過上表我們可以看到,當用戶在按F5進行重新整理的時候,會忽略Expires/Cache-Control的設定,會再次傳送請求去伺服器請求,而Last-Modified/Etag還是有效的,伺服器會根據情況判斷返回304還是200;而當用戶使用Ctrl+F5進行強制重新整理的時候,只是所有的快取機制都將失效,重新從伺服器拉去資源。
一般情況下,使用Cache-Control/Expires會配合Last-Modified/ETag一起使用,因為即使伺服器設定快取時間, 當用戶點選“重新整理”按鈕時,瀏覽器會忽略快取繼續向伺服器傳送請求,這時Last-Modified/ETag將能夠很好利用304,從而減少響應開銷。
不能被快取的請求
無法被瀏覽器快取的請求:
HTTP資訊頭中包含Cache-Control:no-cache,pragma:no-cache(HTTP1.0),或Cache-Control:max-age=0等告訴瀏覽器不用快取的請求
需要根據Cookie,認證資訊等決定輸入內容的動態請求是不能被快取的
經過HTTPS安全加密的請求(有人也經過測試發現,ie其實在頭部加入Cache-Control:max-age資訊,firefox在頭部加入Cache-Control:Public之後,能夠對HTTPS的資源進行快取,參考HTTPS的七個誤解)
POST請求無法被快取
HTTP響應頭中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的請求無法被快取
最後盜圖一張,詳細介紹了HTTP有無快取請求資源時的整個過程。圖片來自文章貓哥網路程式設計系列:詳解 BAT 面試題