HTTP之200還是304?
當(dāng)瀏覽器第一次加載資源的時(shí)候,返回一般為200,意思是成功獲取資源,并會(huì)在瀏覽器的緩存中記錄下max-age,第二次訪問(wèn)的時(shí)候:如果只是用瀏覽器打開(kāi),那么瀏覽器會(huì)去判斷這個(gè)資源在緩存里有沒(méi)有,如果有的話(huà),會(huì)去判斷max-age,看看過(guò)期沒(méi)有,如果沒(méi)有過(guò)期,則直接讀緩存,根本不會(huì)和服務(wù)器進(jìn)行交互,換句話(huà)說(shuō),斷網(wǎng)都能打開(kāi),就和本地跑一樣!如果已經(jīng)過(guò)期了,那就去服務(wù)器請(qǐng)求,等待服務(wù)器響應(yīng),這是很費(fèi)時(shí)間的,服務(wù)器如果發(fā)現(xiàn)資源沒(méi)有改變過(guò),那么就會(huì)返回304,告訴瀏覽器,我沒(méi)變過(guò),你去讀緩存吧,于是瀏覽器也不用從服務(wù)器拉數(shù)據(jù)了,然而,等待服務(wù)器響應(yīng)也是一個(gè)很要命的問(wèn)題,在網(wǎng)速發(fā)達(dá)的今天,等一個(gè)響應(yīng),有時(shí)比下載還慢。如果是用瀏覽器刷新的,那么瀏覽器不會(huì)去判斷max-age了,直接去服務(wù)器拿,如果服務(wù)器判斷資源沒(méi)變過(guò),則還是會(huì)返回304,和上面是一樣的,所以刷新一下,其實(shí)很可怕,等于把所有的資源都要去服務(wù)器請(qǐng)求一邊,問(wèn)問(wèn)服務(wù)器我過(guò)期了沒(méi)有。
瀏覽器在第一次請(qǐng)求資源的時(shí)候,服務(wù)端響應(yīng)頭里可以設(shè)置expires字段,該字段表示該資源的緩存過(guò)期時(shí)間,第二次請(qǐng)求的時(shí)候,如果時(shí)間還在該緩存時(shí)間之內(nèi),則會(huì)直接使用緩存,否則重新加載資源, 這個(gè)expires字段有個(gè)缺陷,就是它必須服務(wù)端和客戶(hù)端的時(shí)間嚴(yán)格同步才能生效,所以現(xiàn)在很多人不會(huì)使用改方案。另外一種方案是第一次請(qǐng)求資源的時(shí)候,服務(wù)端設(shè)置響應(yīng)頭cache-control: max-age,這樣設(shè)置的意思是告訴瀏覽器,這個(gè)資源什么時(shí)候過(guò)期,等第二次請(qǐng)求資源的時(shí)候,判斷是否超出了過(guò)期時(shí)間,如果沒(méi)超出,直接使用緩存。
緩存狀態(tài)碼 200 OK (from cache) 與 304 Not Modified
- 200 OK (from cache) 是瀏覽器沒(méi)有跟服務(wù)器確認(rèn),直接用了瀏覽器緩存;
- 304 Not Modified 是瀏覽器和服務(wù)器多確認(rèn)了一次緩存有效性,再用的緩存。
304 Not Modified:客戶(hù)端有緩沖的文件并發(fā)出了一個(gè)條件性的請(qǐng)求(一般是提供If-Modified-Since頭表示客戶(hù)只想比指定日期更新的文檔)。服務(wù)器告訴客戶(hù),原來(lái)緩沖的文檔還可以繼續(xù)使用。
304 Not Modified 比 200 OK (from cache) 慢,指的是瀏覽器還向服務(wù)器確認(rèn)了下 “If-Not-Modified”,才用的緩存
200和304特點(diǎn)
- 狀態(tài)碼200:請(qǐng)求已成功,請(qǐng)求所希望的響應(yīng)頭或數(shù)據(jù)體將隨此響應(yīng)返回。即返回的數(shù)據(jù)為全量的數(shù)據(jù),如果文件不通過(guò)GZIP壓縮的話(huà),文件是多大,則要有多大傳輸量。
- 狀態(tài)碼304:如果客戶(hù)端發(fā)送了一個(gè)帶條件的 GET 請(qǐng)求且該請(qǐng)求已被允許,而文檔的內(nèi)容(自上次訪問(wèn)以來(lái)或者根據(jù)請(qǐng)求的條件)并沒(méi)有改變,則服務(wù)器應(yīng)當(dāng)返回這個(gè)狀態(tài)碼。即客戶(hù)端和服務(wù)器端只需要傳輸很少的數(shù)據(jù)量來(lái)做文件的校驗(yàn),如果文件沒(méi)有修改過(guò),則不需要返回全量的數(shù)據(jù)。
狀態(tài)為304的請(qǐng)求要比狀態(tài)為200的請(qǐng)求的數(shù)據(jù)量小很多,因?yàn)?04只需要返回響應(yīng)頭,并不需要返回整個(gè)文件,所以只需要幾字節(jié)就可以了,這樣能夠節(jié)省大量的網(wǎng)絡(luò)帶寬,并減少了頁(yè)面的渲染時(shí)間。
什么是瀏覽器緩存?
瀏覽器緩存是為了節(jié)約網(wǎng)絡(luò)的資源加速瀏覽,瀏覽器在用戶(hù)磁盤(pán)上對(duì)最近請(qǐng)求過(guò)的文檔進(jìn)行存儲(chǔ),當(dāng)訪問(wèn)者再次請(qǐng)求這個(gè)頁(yè)面時(shí),瀏覽器就可以從本地磁盤(pán)顯示文檔,這樣就可以加速頁(yè)面的閱覽。瀏覽器緩存主要包括強(qiáng)緩存和協(xié)商緩存。-- 百度百科
瀏覽器緩存也稱(chēng)為http緩存。
通俗地說(shuō),瀏覽器緩存指的是瀏覽器為了節(jié)省網(wǎng)絡(luò)資源及加快頁(yè)面渲染,將請(qǐng)求過(guò)的資源緩存在本地(硬盤(pán)和內(nèi)存中),再根據(jù)http響應(yīng)頭來(lái)判斷是否讀取本地的緩存資源。
緩存HTTP頭信息
- Date:原服務(wù)器發(fā)送該資源響應(yīng)報(bào)文的時(shí)間(GMT格式)
- Age:Age表示這個(gè)響應(yīng)已經(jīng)存活了多久了(HTTP/1.0的響應(yīng)不帶Age)
- Expires:即在 HTTP 頭中指明具體失效的時(shí)間(HTTP/1.0),Expires = HTTP-date
- Pragma:no-cache,每次請(qǐng)求頁(yè)面時(shí)都不要讀緩存,兼容HTTP/1.0,優(yōu)先級(jí)高于Expires(HTTP/1.0 + HTTP/1.1)
- Cache Control:優(yōu)先級(jí)高于Pragma、Expires(HTTP/1.1) 【public,客戶(hù)端和服務(wù)端都可以緩存;private,只能客戶(hù)端緩存;no-store,不使用緩存;no-cache,使用協(xié)商緩存。】
Expires
- Expires是http1.0提出的一個(gè)表示資源過(guò)期時(shí)間的header,它描述的是一個(gè)絕對(duì)時(shí)間,由服務(wù)器返回。
- Expires第二次請(qǐng)求時(shí),將和本地時(shí)間比對(duì)。
- Expires 第一次請(qǐng)求服務(wù)器是,響應(yīng)頭會(huì)返回一個(gè)Expires的文件過(guò)期時(shí)間。
- Expires 第二次請(qǐng)求,客戶(hù)端使用本地時(shí)間和文件的過(guò)期時(shí)間進(jìn)行比對(duì),如果文件未過(guò)期則直接使用本地緩存,返回狀態(tài)碼200(from memory cache)或200(from disk cache)。
Expires Cache-Control
- Cache-Control: no-cache 必須先與代理服務(wù)器確認(rèn)是否更改,然后在在決定使用緩存還是請(qǐng)求,類(lèi)似于協(xié)商緩存(304)
- Cache-Control: no-store 才是真正的不緩存數(shù)據(jù)到本地
- Cache-Control: public 可以被所有用戶(hù)緩存(多用戶(hù)共享),包括終端和CDN等中間代理服務(wù)器
- Cache-Control: private 只能被終端瀏覽器緩存(而且是私有緩存),不允許中繼緩存服務(wù)器進(jìn)行緩存
- Cache-Control: must-revalidate如果緩存內(nèi)容失效,請(qǐng)求必須發(fā)送服務(wù)器進(jìn)行驗(yàn)證
- Cache-Control: max-age=s 緩存內(nèi)容在s秒后失效,僅HTTP1.1可用 max-gae 第一次請(qǐng)求服務(wù)器時(shí),響應(yīng)頭會(huì)返回一個(gè) max-age,是文件多少時(shí)間后過(guò)期。max-gae 第二次請(qǐng)求,客戶(hù)端會(huì)校驗(yàn)文件是否過(guò)期,如果文件未過(guò)期則直接使用本地緩存,返回狀態(tài)碼200(from memory cache)或200(from disk cache)。
強(qiáng)緩存(200 from disk cache 或者 200 from memory cache, 硬盤(pán)緩存和內(nèi)存緩存)
強(qiáng)緩存指的是瀏覽器緩存了該請(qǐng)求的資源且根據(jù)緩存標(biāo)識(shí)(響應(yīng)頭的cache-control:max-age 及 expires)判定資源未過(guò)期。
協(xié)商緩存(304)
協(xié)商緩存指的是瀏覽器緩存了該請(qǐng)求的資源但未能判斷資源是否過(guò)期,需要向服務(wù)器發(fā)起攜帶緩存標(biāo)識(shí)(響應(yīng)頭的 last-modified 及 Etag)的請(qǐng)求來(lái)詢(xún)問(wèn)資源是否過(guò)期。
瀏覽器緩存相關(guān)響應(yīng)頭
強(qiáng)緩存相關(guān)
Expires
Expires 是 HTTP/1.0 中控制瀏覽器緩存的字段,其值為服務(wù)器返回的該請(qǐng)求結(jié)果緩存的到期時(shí)間。當(dāng)瀏覽器再次發(fā)起請(qǐng)求時(shí),如果客戶(hù)端時(shí)間早于到期時(shí)間,則直接讀取緩存結(jié)果而不用再次發(fā)起請(qǐng)求。
因?yàn)檫@邊用到了客戶(hù)端的時(shí)間進(jìn)行判斷,所以可能會(huì)因?yàn)榭蛻?hù)端時(shí)間和服務(wù)器時(shí)間不同導(dǎo)致預(yù)期之外的緩存行為
Cache-Control
cache-control 是HTTP/1.1 的字段,是控制緩存的重要規(guī)則,它的值包含了幾組可選的值,中間使用逗號(hào)相隔開(kāi)
- 控制代理服務(wù)器緩存
- public:所有內(nèi)容被緩存(代理服務(wù)器和客戶(hù)端都可緩存)
- private(默認(rèn)):僅客戶(hù)端可緩存
- 控制是否緩存
- no-cache:客戶(hù)端緩存內(nèi)容,是否使用緩存需要與服務(wù)器進(jìn)行協(xié)商
- no-store:所有內(nèi)容不會(huì)被緩存
- 控制緩存有效期
- max-age:其值為資源的有效時(shí)間(秒),表示緩存將在xx秒后失效
當(dāng) max-age 和 expires 同時(shí)存在時(shí),只有 max-age 有效,expires 將不再生效
協(xié)商緩存相關(guān)
last-modified
last-modified 用于標(biāo)識(shí)資源的最新修改時(shí)間,用于標(biāo)記內(nèi)容是否更新。
響應(yīng)頭的 last-modified 會(huì)作為緩存標(biāo)識(shí)一起存儲(chǔ)與緩存中,當(dāng)再次請(qǐng)求需要進(jìn)行服務(wù)器協(xié)商時(shí),請(qǐng)求頭的 if-Modified-Since 字段將帶上緩存中的 last-modified 的值與服務(wù)器的資源修改時(shí)間進(jìn)行比較,判斷緩存是否過(guò)期。
使用 last-modified 有可能出現(xiàn)服務(wù)器資源未產(chǎn)生實(shí)質(zhì)性更新但是修改時(shí)間更新的情況,比如資源重寫(xiě)。
Etag
Etag 是服務(wù)器根據(jù)資源內(nèi)容通過(guò)一系列算法計(jì)算得出的用于標(biāo)識(shí)資源的字符串編碼,當(dāng)資源內(nèi)容有修改時(shí),對(duì)應(yīng)的 Etag 也會(huì)更新。其主要功能就是用于標(biāo)記資源內(nèi)容是否更新。
同樣的,響應(yīng)頭的 Etag 會(huì)作為緩存標(biāo)識(shí)一起存儲(chǔ)于緩存中,當(dāng)再次請(qǐng)求需要進(jìn)行服務(wù)器協(xié)商時(shí),請(qǐng)求頭的 if-None-Match 字段將帶上緩存中的 etag 值與服務(wù)器上資源的 Etag 進(jìn)行比較,判斷緩存是否過(guò)期。
當(dāng) Etag 和 last-modified 同時(shí)出現(xiàn)時(shí),以 Etag 的標(biāo)識(shí)為準(zhǔn)。
瀏覽器緩存相關(guān)的請(qǐng)求頭
Cache-Control
- no-cache:強(qiáng)制向源服務(wù)器再次驗(yàn)證,控制代理服務(wù)器不能直接返回緩存
- max-age:響應(yīng)的最大age值,可以設(shè)置max-age=0使用協(xié)商緩存
last-modified-since
配合 last-modified 使用
if-None-Match
配置 Etag 使用
完整的瀏覽器緩存過(guò)程
- 判斷本地是否有緩存,如果本地沒(méi)有緩存資源則進(jìn)入第4步,如果有則進(jìn)入第2步。
- 通過(guò) expires 和 max-age 判斷緩存是否過(guò)期,如果未過(guò)期則直接返回資源(200 from cache)。否則進(jìn)入第3步。
- 攜帶緩存標(biāo)識(shí)(if-modified-since if-None-Match)進(jìn)行請(qǐng)求,與服務(wù)器協(xié)商驗(yàn)證緩存是否過(guò)期,如果未過(guò)期則服務(wù)器返回不帶資源實(shí)體的響應(yīng),瀏覽器從緩存中獲取資源并返回給前端(304),否則進(jìn)入第4步。
4.正常請(qǐng)求資源,服務(wù)器返回資源(200)并進(jìn)行緩存。
緩存相關(guān)知識(shí)點(diǎn)
緩存的位置
瀏覽器緩存的存儲(chǔ)位置分為硬盤(pán)(disk cache)和內(nèi)存(memory cache),瀏覽器的讀取順序?yàn)閮?nèi)存->硬盤(pán)。
不同的資源可能會(huì)緩存在不同的位置
- css:因?yàn)?css 解析構(gòu)建 CSSOM 樹(shù)之后就不需要使用了,所以會(huì)被緩存在硬盤(pán)中,讀取時(shí)從硬盤(pán)讀取。
- JS 和圖片:因?yàn)闉g覽器解析JS和圖片之后,會(huì)將其放在內(nèi)存中,所以?xún)?yōu)先讀取內(nèi)存(實(shí)踐證明并非一定),當(dāng)打開(kāi)新窗口或者瀏覽器時(shí)則讀取硬盤(pán)緩存。
不同的用戶(hù)行為導(dǎo)致的緩存問(wèn)題
刷新(F5/Command+r)
刷新時(shí)會(huì)給文檔請(qǐng)求頭加上 Cache-Control:max-age=0,所以文檔一般會(huì)觸發(fā)協(xié)商緩存。而文檔之外的其他資源(JS css)則正常請(qǐng)求(可能會(huì)觸發(fā)強(qiáng)緩存或協(xié)商緩存)。
強(qiáng)制刷新(CTRL + F5/Command+shift+r/勾選disable cache)
強(qiáng)制刷新時(shí)會(huì)給請(qǐng)求頭加上 Cache-control:no-cache,使用協(xié)商緩存,但同時(shí)會(huì)刪除協(xié)商緩存字段(if-modified-since if-none-match),所以最終結(jié)果都是從服務(wù)器請(qǐng)求新資源。
關(guān)閉瀏覽器/打開(kāi)新標(biāo)簽
沒(méi)有特殊操作,有可能使用強(qiáng)緩存或協(xié)商緩存。