HTTP 協(xié)議的緩存機(jī)制涉及到多個(gè)請求頭字段,而且整個(gè)緩存機(jī)制的細(xì)節(jié)行為也存在各種情況的差異速警,譬如說什么時(shí)候訪問本地緩存不發(fā)送請求叹誉,什么時(shí)候發(fā)送請求查看資源是否更新,獲取 response 什么情況下更新緩存等闷旧。以前我對此一知半解只是籠統(tǒng)的知道一些概念长豁,譬如 Cache-Control 可以控制緩存的時(shí)間和是否需要緩存,但是緩存過期后的行為忙灼,有緩存后瀏覽器是否有 http 請求都不甚了解匠襟。所以特地 google 下,此篇是對此的知識梳理。
協(xié)議概述
什么情況下可以使用本地緩存酸舍?譬如說我們用 get 方式請求了一個(gè)資源 http://mytest.domain.com/static/images/bg.png
机错,那么我們下次再請求這個(gè)圖片資源的時(shí)候符合哪些條件可以使用本地緩存呢?
- url 必須是
http://mytest.domain.com/static/images/bg.png
父腕,如果是http://mytest.domain.com/static/images/bg.png?t=12312321
就會發(fā)起新的請求弱匪,因?yàn)?url 不同。 - 發(fā)送請求的 method 必須可被緩存璧亮,譬如 get萧诫。
- 第一次請求 response(即本地緩存)如果有一個(gè) Vary 頭,他的值列出的是一系列 http header枝嘶,第二次請求的請求頭中那些在 Vary 值中所列的頭帘饶,必須和第一次請求相同(具體規(guī)則)。
- 第二次請求不包含請求頭 Pragma: no-cache
- 第二次請求不包含請求頭 Cache-Control: no-cache|max-age=0
- 第一次請求的 response(即本地緩存)不包含 Cache-Control: no-cache
- 本地緩存沒有過期
- 或者雖然本地緩存已經(jīng)過期群扶,但是服務(wù)器驗(yàn)證緩存和服務(wù)器資源一致及刻,允許使用本地緩存的情況(即獲得304 response)
注1:上述任何提一個(gè)條件都可以被 cache-control extension 覆蓋
注2:response header中不僅僅可以 Cache-Control: no-cache,還可以 Cache-Control: no-cache="Set-Cookie" 詳見
如何計(jì)算本地緩存是否過期
瀏覽器是通過比較緩存剩余有效時(shí)間和當(dāng)前緩存已存在時(shí)間來判斷的:response_is_fresh = (freshness_lifetime > current_age)
竞阐。freshness_lifetime
取值優(yōu)先級次序如下列表所示(排在上面的優(yōu)先級越高):
- Cache-Control: s-maxage=xx
- Cache-Control: max-age=xx
- Expires: xxxxx
- 按規(guī)則進(jìn)行計(jì)算(推測)
注1缴饭;如果有多個(gè)重復(fù)的上述頭,那么是非法的骆莹,視作 response(資源)過期
freshness_lifetime 計(jì)算(推測)規(guī)則:
規(guī)范并沒有給出具體的算法颗搂,但是給出了最壞情況(but does impose worst-case constraints on their results),如果 response(資源)有 Last-Modified 頭幕垦,那么推薦用從當(dāng)前到lastmodified這個(gè)時(shí)間段的 10% 作為 freshness_lifetime
丢氢,并且 response(資源)的 current_age 如果已經(jīng)超過24小時(shí),必須在這個(gè) response 上加上113 warn-code頭先改。很少有瀏覽器實(shí)現(xiàn)了freshness_lifetime
的自助計(jì)算(推測)疚察,所以還是還是鼓勵(lì)給出上述顯式的1 - 3三種情況。
current_age 計(jì)算規(guī)則:
泛泛來說就是 response(資源)在本地的駐足時(shí)間加上網(wǎng)絡(luò)傳輸時(shí)間仇奶,因?yàn)榫W(wǎng)絡(luò)傳輸時(shí)間的計(jì)算有多個(gè)條件貌嫡,規(guī)范實(shí)在看的我頭暈,所以具體計(jì)算規(guī)則詳見規(guī)范
本地緩存過期猜嘱,如何通過服務(wù)器驗(yàn)證緩存的是否依然有效(即304的情況)
首先衅枫,如果第一次的 response(資源)頭中顯示申明了一些禁止緩存的頭(譬如:"no-store" or "no-cache" 等等),就不存在過期不過期的問題朗伶,因?yàn)檫@個(gè)資源不允許緩存弦撩。其次,過期緩存也不一定不可用论皆,如果在斷網(wǎng)或者第二次請求帶上 max-stale 這個(gè)請求頭益楼,那么瀏覽器可以使用過期的緩存(masx-stale 表示在緩存過期后多少時(shí)間內(nèi)瀏覽器依然可以使用緩存)猾漫。碰到過期緩存,瀏覽器可以發(fā)送一個(gè)條件請求(conditional request)感凤。這個(gè)請求的 url 依然是第一次請求的 url悯周,只是會帶上些當(dāng)前資源的一些信息,以供服務(wù)器驗(yàn)證這個(gè)緩存是否依然可用還是需要更新:
- response(資源)的 Last-Modified 頭所帶的值會放到條件請求的 If-Modified-Since 頭中陪竿,或者是 If-Unmodified-Since 又或者 If-Range禽翼。
- response(資源)的 ETag 頭所帶的值會放到條件請求的 If-None-Match 頭中,或者 If-Match 又或者 If-Range族跛。
服務(wù)器會根據(jù)不同的條件請求頭來驗(yàn)證資源闰挡,具體的行為詳見規(guī)范,這里不細(xì)致展開礁哄。針對條件請求长酗,服務(wù)器返回會有三種情況:
- 一個(gè)帶有304 status code 的返回。表示緩存可以被更新和重用桐绒。
- 一個(gè)帶有 body 的完整的 response夺脾。表示用這個(gè) response 作為請求的返回,并且視條件可以用這個(gè)完整的 response 替換瀏覽器原有的緩存(此資源)茉继。
- 如果返回一個(gè)5xx的 response咧叭,那么瀏覽器可以選擇就顯示這個(gè)5xx的返回,或者使用本地緩存(盡管可能是過期的)- 規(guī)范沒有規(guī)定應(yīng)該選擇哪種處理馒疹,應(yīng)該是取決于瀏覽器的行為佳簸。
如果是一個(gè)304的返回乙墙,規(guī)范說這個(gè)返回可以更新本地緩存颖变,更新策略分三種:
- 如果這個(gè)304 response 帶有資源有效性的強(qiáng)驗(yàn)證頭,那么瀏覽器會尋找本地緩存听想,尋找那些帶有同樣強(qiáng)驗(yàn)證頭的緩存腥刹,然后用這個(gè)最新的 response 去更新這些匹配的緩存(同一個(gè)資源可能瀏覽器保存有多份緩存,譬如日期不同等)汉买。
- 如果這個(gè)304 response 帶有資源有效性的弱驗(yàn)證頭衔峰,那么瀏覽器同樣會找相匹配的緩存,但是只會更新最新的那條匹配的緩存蛙粘。
- 如果這個(gè)304 response 沒有帶有任何資源有效性驗(yàn)證頭垫卤,并且瀏覽器緩存只有一份,并且這份也同樣沒帶有任何資源有效性驗(yàn)證頭出牧,那么瀏覽器就會用這個(gè)304 response穴肘,更新本地緩存。
協(xié)議流程圖
假設(shè)第一次請求一個(gè)資源舔痕,返回 header 里面帶上如下字段:
Cache-Control: max-age=600
Last-Modified: Wed, 28 Aug 2013 10:36:42 GMT
ETag: "124752e0d85461a16e76fbdef2e84fb9"
拋開細(xì)枝末節(jié)的東西评抚,那么第二次請求通常大致流程圖如下:
當(dāng)前資源緩存是否過期:response_is_fresh = (freshness_lifetime > current_age)
|
-----------------------------------
| |
是 否
| |
發(fā)送請求豹缀,帶上請求頭 從本地緩存中獲取資源(不發(fā)請求)
If-Modified-Since: 此資源Last-Modified的值
If-None-Match: 此資源ETag的值
|
服務(wù)器根據(jù) If-Modified-Since 和 If-None-Match
兩個(gè)值判斷資源是否更新過
|
-------------------------
| |
是 否
| |
返回一個(gè) status code:200 的 response 返回一個(gè) status code:304 的 response
response body 里面是請求的資源 response body 為空
| |
瀏覽器用 response body里面的資源 依然從本地緩存里面獲取資源
替換本地緩存中的資源
特殊情況
當(dāng)你去瀏覽器驗(yàn)證的時(shí)候可能會碰到一些特殊情況,就是緩存有效慨代,但是你刷新瀏覽器依然發(fā)送的條件請求邢笙。其實(shí)是因?yàn)闉g覽器在請求頭中加入了一些料,譬如: Cache-Control: max-age=0侍匙。你刷新的方式可以有很多種氮惯,譬如:按F5,按ctrl+F5想暗,在地址欄按回車等等筐骇。這些不同的行為都會影響瀏覽器發(fā)送請求的行為。這里有一些參考《在瀏覽器地址欄按回車江滨、F5铛纬、Ctrl+F5刷新網(wǎng)頁的區(qū)別》