概述
瀏覽器http緩存靴拱,既是網(wǎng)頁(yè)靜態(tài)資源服務(wù)性能優(yōu)化的一把利器国觉,也是無(wú)數(shù)web開(kāi)發(fā)者在工作之初談之色變的一大難題。
在開(kāi)發(fā)過(guò)程中我們極力避免緩存又憨,但在生產(chǎn)環(huán)境中翠霍,我們又在想盡辦法利用緩存。
所以了解瀏覽器緩存的機(jī)制蠢莺,是一個(gè)優(yōu)秀開(kāi)發(fā)者繞不開(kāi)的重要基礎(chǔ)知識(shí)寒匙。
各種緩存的命中與否,說(shuō)到底不過(guò)是幾個(gè)與之相關(guān)的http header數(shù)據(jù)的匹配與校驗(yàn)躏将。如果了解了每個(gè)相關(guān)header的意義與關(guān)系锄弱,那么就能將緩存策略運(yùn)用自如考蕾。
cache type
- 200 from cache
直接從本地緩存(有的文章稱(chēng)之為強(qiáng)緩存)中獲取響應(yīng),返回200狀態(tài)棵癣,chrome網(wǎng)絡(luò)面板中的size項(xiàng)顯示from cache
最快速辕翰,最省流量夺衍,因?yàn)楦緵](méi)有向服務(wù)器發(fā)送請(qǐng)求狈谊。
-
304 Not Modified
在本地緩存沒(méi)有命中的情況下,請(qǐng)求頭中發(fā)送一定的校驗(yàn)數(shù)據(jù)到服務(wù)端沟沙,校驗(yàn)成功后服務(wù)器返回304 not modified表示資源未被修改河劝,瀏覽器從本地緩存中獲取響應(yīng),這種緩存方式通常被稱(chēng)為協(xié)商緩存矛紫。
快速赎瞎,發(fā)送的數(shù)據(jù)很少,只返回一些基本的響應(yīng)頭信息颊咬,數(shù)據(jù)量很小务甥,不發(fā)送實(shí)際響應(yīng)體。
cache-304.png 200 OK
以上兩種緩存全都失敗喳篇,服務(wù)器返回完整響應(yīng)敞临。
沒(méi)有用到緩存,相對(duì)最慢麸澜。
本地緩存
本地緩存的使用挺尿,相當(dāng)于基于之前服務(wù)器的response header,瀏覽器認(rèn)為當(dāng)前的緩存可以直接使用炊邦,于是不再與服務(wù)器進(jìn)行任何交互而直接使用緩存的過(guò)程编矾。
與本地緩存命中相關(guān)的http header:
- Pragma
這個(gè)是http1.0時(shí)代的遺留產(chǎn)物,該字段被設(shè)置為no-cache時(shí)(實(shí)際上現(xiàn)有的RFC標(biāo)準(zhǔn)標(biāo)明只有這個(gè)可選值)馁害,會(huì)告知瀏覽器禁用本地緩存窄俏,即每次都向服務(wù)器發(fā)送請(qǐng)求。
- Expires
http1.0時(shí)代用來(lái)啟用本地緩存的字段碘菜,expires值對(duì)應(yīng)一個(gè)形如Thu, 31 Dec 2037 23:55:55 GMT的格林威治時(shí)間凹蜈,告訴瀏覽器緩存實(shí)現(xiàn)的時(shí)刻,如果還沒(méi)到該時(shí)刻炉媒,標(biāo)明緩存有效踪区,無(wú)需發(fā)送請(qǐng)求。
但是這個(gè)方式有個(gè)很明顯的問(wèn)題吊骤,就是瀏覽器與服務(wù)器的時(shí)間是不能保證一致的缎岗,如果時(shí)間差距較大,那么會(huì)影響緩存管理的結(jié)果白粉。
# example:
# vim /etc/nginx/sites-available/your_server_config
location ~* \.(?:css|js)$ {
expires 1d;
access_log off;
add_header Cache-Control "public";
}
這告訴瀏覽器,在date+1號(hào)之前,可以直接使用該文本的緩存副本传泊。但是鼠渺,可能會(huì)因?yàn)榉?wù)器和客戶(hù)端的GMT時(shí)間不同,會(huì)有一定的bug眷细。 所以拦盹,這里只提議在長(zhǎng)時(shí)間緩存的情況下使用。否則溪椎,應(yīng)該選擇Cache-Control
- Cache-Control
Cache-Control 頭在 HTTP/1.1 規(guī)范中定義普舆,取代了之前用來(lái)定義響應(yīng)緩存策略的頭(例如 Expires)。當(dāng)前的所有瀏覽器都支持 Cache-Control校读,因此沼侣,使用它就夠了。
不過(guò)歉秫,目前大部分服務(wù)器都會(huì)將兩者添加上蛾洛,因?yàn)镠TTP規(guī)定,如果Cache-Control和expires同時(shí)出現(xiàn)的話雁芙,expires會(huì)默認(rèn)被覆蓋掉轧膘。 此時(shí),返回的響應(yīng)碼不再是304(文件未改動(dòng)),而是200(資源成功訪問(wèn)).
http1.1針對(duì)Expires時(shí)間不一致的問(wèn)題兔甘,采取了一個(gè)十分聰明的設(shè)定谎碍,運(yùn)用Cache-Control來(lái)告知瀏覽器緩存過(guò)期的時(shí)間間隔而不是時(shí)刻,那么即使具體時(shí)間不一致裂明,也不影響緩存的管理椿浓。
Cache-Control允許的值如下:
no-store
禁止瀏覽器緩存響應(yīng),通常一些非常隱私的數(shù)據(jù)會(huì)啟用這個(gè)值
no-cache
不允許直接使用本地緩存闽晦,必須先發(fā)起請(qǐng)求和服務(wù)器協(xié)商扳碍。
max-age=seconds
告知瀏覽器該響應(yīng)本地緩存有效的最長(zhǎng)期限,以秒為單位仙蛉。
其他可選值不常見(jiàn)笋敞,以后遇到再補(bǔ)充
public: 共有緩存,可被緩存代理服務(wù)器緩存,比如CDN
private: 私有緩存荠瘪,不能被共有緩存代理服務(wù)器緩存夯巷,可被用戶(hù)的代理緩存如瀏覽器。
max-age=[秒]:表示在這個(gè)時(shí)間范圍內(nèi)緩存是新鮮的無(wú)需更新哀墓。類(lèi)似Expires時(shí)間趁餐,不過(guò)這個(gè)時(shí)間是相對(duì)的,而不是絕對(duì)的篮绰。也就是某次請(qǐng)求成功后多少秒內(nèi)緩存是新鮮的后雷。
s-maxage=[秒]:類(lèi)似max-age, 除了僅應(yīng)用于共享緩存(如代理)。
no-cache:這里不是不緩存的意思,只是每次在使用緩存之前都強(qiáng)制發(fā)送請(qǐng)求給源服務(wù)器進(jìn)行驗(yàn)證臀突,檢查文件該沒(méi)改變(其實(shí)這里和ETag/Last區(qū)別不大)
no-store:就是禁止緩存勉抓,不讓瀏覽器保留緩存副本
must-revalidate:告訴瀏覽器,你這必須再次驗(yàn)證檢查信息是否過(guò)期, 返回的代號(hào)就不是200而是304了候学。
proxy-revalidate:類(lèi)似must-revalidate藕筋,除了只能應(yīng)用于代理緩存。
比如梳码,這里我可以設(shè)置Cache-Control為:
#Response Headers
Cache-Control:private, max-age=0, must-revalidate
該文件是一個(gè)私有文件,只能被瀏覽器緩存收捣,而不能被代理緩存枫攀。max-age標(biāo)識(shí)該緩存立即過(guò)期肋层,其實(shí)和no-cache實(shí)際上區(qū)別不大. 然后must-revalidate告訴瀏覽器橄妆,你必須給我再驗(yàn)證文件過(guò)沒(méi)過(guò)期,比如接下來(lái)可能會(huì)驗(yàn)證Last-Modified或者ETag.如果沒(méi)有過(guò)期則使用本地緩存. 其實(shí)上面可以直接等同于:
#Response Headers
Cache-Control:private,no-cache
這三個(gè)字段主要用來(lái)告知瀏覽器的本地緩存管理策略符匾,優(yōu)先級(jí)為Pragma > Cache-Control > Expires。
協(xié)商緩存
當(dāng)瀏覽器沒(méi)有命中本地緩存瘩例,如本地緩存過(guò)期或者響應(yīng)中聲明不允許直接使用本地緩存啊胶,那么瀏覽器肯定會(huì)發(fā)起請(qǐng)求。
在http緩存模型中垛贤,即使瀏覽器向服務(wù)器發(fā)起請(qǐng)求焰坪,服務(wù)器也不一定要返回整個(gè)資源的實(shí)體內(nèi)容。而可以返回協(xié)商結(jié)果:“瀏覽器聘惦,我的資源沒(méi)有修改過(guò)某饰,你可以直接使用你的本地緩存”。
很顯然善绎,服務(wù)器要判斷瀏覽器的緩存是否可用黔漂,那么必須瀏覽器告訴服務(wù)器一些自己緩存的信息,所以協(xié)商緩存相關(guān)的header字段禀酱,必然是成對(duì)出現(xiàn)的炬守。
Last-Modified
格式:Response Headers
: Last-Modified: Tue, 08 Nov 2016 01:50:36 GMT
告訴瀏覽器資源的最后修改時(shí)間,相當(dāng)于對(duì)資源進(jìn)行了版本管理剂跟,至于這個(gè)時(shí)間怎么生成的减途,那是服務(wù)器的事兒,不在這里討論曹洽。
得知資源的最后修改時(shí)間后鳍置,客戶(hù)端會(huì)將這個(gè)信息提交到服務(wù)器做檢查,如果服務(wù)器驗(yàn)證出最后修改時(shí)間是一致的送淆,那么表示該資源沒(méi)有修改過(guò)税产,可以返回304狀態(tài)。
瀏覽器請(qǐng)求頭中標(biāo)記最終修改時(shí)間的header字段:
Request Headers
: If-Modified-Since: Thu, 31 Mar 2016 07:07:52 GMT
ETag
通常情況下,服務(wù)器默認(rèn)是打開(kāi)Etag的砖第,但是為了防止配置文件不正確撤卢,關(guān)閉了Etag,這時(shí)候梧兼,就需要你對(duì)對(duì)配置文件做一些設(shè)置放吩。 這里我以Nginx為例: 打開(kāi)ngnix.conf文件,檢查是否有以下語(yǔ)句:()
etag off;
more_set_headers -s 404 -t 'ETag';
more_clear_headers 'Etag';
即使我沒(méi)有討論服務(wù)器怎么生成最終修改時(shí)間羽杰,也可以相見(jiàn)渡紫,這個(gè)模式會(huì)存在不準(zhǔn)確的問(wèn)題:如果資源明明沒(méi)有改變,但是Last-Modified發(fā)生了變化考赛,那么就會(huì)返回整個(gè)資源實(shí)體惕澎。
針對(duì)這個(gè)問(wèn)題,http1.1還推出了ETag字段颜骤,服務(wù)器會(huì)根據(jù)某種計(jì)算方式(常見(jiàn)的如md5)給出一個(gè)標(biāo)識(shí)符唧喉,這個(gè)標(biāo)識(shí)符其實(shí)標(biāo)記的是資源的實(shí)際內(nèi)容。
格式:Response Headers
: ETag:"58212f6c-22f23"
檢測(cè)過(guò)程與Last-Modified類(lèi)似忍抽,瀏覽器請(qǐng)求頭中標(biāo)記ETag的字段:
Request Headers
: If-None-Match:"58212f6c-22f23"
瀏覽器對(duì)緩存的影響
注意觀察瀏覽器行為的開(kāi)發(fā)者很容易發(fā)現(xiàn)八孝,輸入url訪問(wèn)與f5刷新,各個(gè)資源的請(qǐng)求速度好像不太相同鸠项。
常見(jiàn)的瀏覽器會(huì)將訪問(wèn)行為分為3種:
地址欄輸入U(xiǎn)RL或書(shū)簽訪問(wèn)
按照正常策略使用緩存按照正常策略使用緩存
按照正常策略使用緩存Ctrl+F5
跳過(guò)強(qiáng)緩存與協(xié)商緩存干跛,直接加載資源實(shí)體
具體的實(shí)現(xiàn)方式可以想見(jiàn)的是發(fā)送不同的請(qǐng)求頭~~
緩存策略的選擇
對(duì)大多數(shù)站點(diǎn)來(lái)說(shuō),以下內(nèi)容是非常適合緩存的:
普通不變的圖像祟绊,如logo楼入,圖標(biāo)等
js、css靜態(tài)文件
可下載的內(nèi)容牧抽,媒體文件
這些文件很少改變嘉熊,適合長(zhǎng)時(shí)間強(qiáng)緩存。
以下內(nèi)容是做緩存時(shí)需要注意的阎姥,建議主要使用協(xié)商緩存的:
HTML文件
經(jīng)常替換的圖片
經(jīng)常修改的js记舆、css文件
其中,js呼巴、css文件可以通過(guò)md5修改文件名的方式改變url來(lái)失效緩存泽腮,
即在文件內(nèi)容變化后將main.95d21235.css改為main.1bcbf5de.css,由于url變化衣赶,所以不存在緩存的問(wèn)題诊赊。
以下內(nèi)容從來(lái)都不應(yīng)該使用緩存:
用戶(hù)隱私等敏感數(shù)據(jù)
經(jīng)常改變的api數(shù)據(jù)接口
其中,后臺(tái)rest api數(shù)據(jù)接口的如果需要引入緩存策略府瞄,必須要進(jìn)行比較謹(jǐn)慎的規(guī)劃碧磅,
將頻繁改變的接口與基本不變的接口區(qū)分碘箍,并且在應(yīng)用服務(wù)器中實(shí)現(xiàn)Last-Modified/ETag的生成機(jī)制以保證緩存不會(huì)造成錯(cuò)誤的結(jié)果。
從這里延伸出去的話鲸郊,理想情況下丰榴,一切網(wǎng)絡(luò)資源都應(yīng)該盡可能選擇不同策略的緩存,但考慮到開(kāi)發(fā)的成本與難度秆撮,這在現(xiàn)實(shí)中很難發(fā)生四濒,因此應(yīng)該嘗試設(shè)置一些明智的緩存策略(最常見(jiàn)的就是給大量的靜態(tài)圖片設(shè)置緩存),以在長(zhǎng)期緩存和站點(diǎn)改變的需求間達(dá)到平衡职辨。
nginx配置緩存策略
-
強(qiáng)緩存
- add_header指令
Syntax: add_header name value [always]; Default: Context: http, server, location, if in location
- expires
Syntax: expires [modified] time; expires epoch | max | off; Default: expires off; Context: http, server, location, if in location
# example: # vim /etc/nginx/sites-available/your_server_config location ~* \.(?:css|js)$ { expires 1d; access_log off; add_header Cache-Control "public"; }
expires為負(fù)值時(shí)盗蟆,表示Cache-Control: no-cache; 當(dāng)為正或者0時(shí),就表示Cache-Control: max-age=指定的時(shí)間(秒); 當(dāng)為max時(shí)舒裤,會(huì)把Expires設(shè)置為 “Thu, 31 Dec 2037 23:55:55 GMT”喳资, Cache-Control 設(shè)置到 10 年;
通過(guò)expires設(shè)置過(guò)期時(shí)間為一天,此時(shí)腾供,服務(wù)器會(huì)根據(jù)當(dāng)前的時(shí)間仆邓,(此處2018-09-06設(shè)置的)加上一天.同時(shí)添加Expires和Cache-Control頭部標(biāo)簽。 即,得到的Response Header為:
Expires: Fri, 07 Sep 2018 07:09:30 GMT
Cache-Control: max-age=86400 //24*60*60
(HTTP規(guī)定台腥,如果出現(xiàn)max-age和expires宏赘,則max-age默認(rèn)覆蓋掉expires) 當(dāng)expires為負(fù)數(shù)表示no-cache,正數(shù)或零表示max-age=time黎侈。 如果你不想緩存,可以直接設(shè)置:
expires -1; //永遠(yuǎn)過(guò)期闷游,Cache-Control: no-cache
詳細(xì)可以直接參閱:nginx配置
- 協(xié)商緩存
- ETag
Syntax: etag on | off;
Default: etag on;
Context: http, server, location
- Last-Modified
add_header指令峻汉,默認(rèn)開(kāi)啟
vim /etc/nginx/nginx.conf
http {
etag on;
##
# Other Settings
##
}
etag on;
擴(kuò)展
##設(shè)置no-cache
//Nginx
expires -1;
//cache-control
Cache-Control:no-cache
##設(shè)置max-age=0
//Nginx
expires 0;
//cache-control
Cache-Control:max-age=0
##設(shè)置其他頭部
//nginx
add_header Cache-Control "no-cache";
add_header Pragma no-cache;
上面說(shuō)的基本上是服務(wù)器的響應(yīng)頭,那在瀏覽器的Request headers里存在cache-control代表什么呢脐往? 當(dāng)請(qǐng)求頭中有:Cache-Control: max-age=0,表示緩存需要進(jìn)行驗(yàn)證(ETag||Last-Modified)休吠,如果緩存未過(guò)期,則可以使用。 當(dāng)請(qǐng)求頭中有:Cache-Control: no-cache,表示瀏覽器只能獲取最新的文件业簿。 和Response Header中的no-store相對(duì)應(yīng)瘤礁。
組合拳法之緩存策略
上面介紹的last/ETag/Expires/Cache都是HTTP協(xié)議的緩存策略。當(dāng)然梅尤,緩存不止這一種柜思,比如在HTML 4.0中定義的某些meta也可以實(shí)現(xiàn)自定義緩存的
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
但,實(shí)際情況是巷燥,這些meta只能在file:// 本地文件中使用赡盘,如果是服務(wù)器則默認(rèn)被覆蓋。現(xiàn)在目前主流的就是使用HTTP1.1協(xié)議緩存 不過(guò)我們一般都不會(huì)單獨(dú)使用某一項(xiàng)缰揪。 但是陨享,組合之后他們的效果是怎樣的呢?
清除緩存
來(lái)源統(tǒng)計(jì)之 Referrer 操作
Referrer 頭里面會(huì)帶上本次請(qǐng)求發(fā)送的 domain,比如:
Referer: https://now.qq.com/qq/market/index.html
不過(guò)抛姑,并不是所有的資源都會(huì)帶上 referrer 頭赞厕,比如,HTML 資源定硝。默認(rèn)情況下皿桑,HTML 的發(fā)送一般會(huì)遵循 No Referrer When Downgrade 的策略,即僅當(dāng)發(fā)生協(xié)議降級(jí)(如 HTTPS 頁(yè)面引入 HTTP 資源喷斋,從 HTTPS 頁(yè)面跳到 HTTP 等)時(shí)不發(fā)送 Referrer 信息唁毒。。而這個(gè)策略的定義星爪,主要根據(jù) referrerPolicy 這個(gè)請(qǐng)求頭來(lái)定制浆西。其基本屬性值為:
no-referrer: 不發(fā)送 referrer 頭。
no-referrer-when-downgrade[default]: 當(dāng)發(fā)生協(xié)議降級(jí)時(shí)采用顽腾,(如 HTTPS 頁(yè)面引入 HTTP 資源近零,從 HTTPS 頁(yè)面跳到 HTTP 等)。
origin: 值發(fā)送 host
origin-when-crossorigin: 在跨域的時(shí)候只發(fā)送 host抄肖,其余是完整 url久信。
unsafe-url: 都發(fā)送 referrer 信息。
referrerPolicy 內(nèi)容設(shè)置可以通過(guò) Response 中的 Referrer-Policy 來(lái)確定漓摩。它可以手動(dòng)指定 referrer 策略裙士。基本內(nèi)容為:
Referrer-Policy: referrer no-referrer|no-referrer-when-downgrade|origin|origin-when-cross-origin|unsafe-url;
不過(guò)管毙,在發(fā)送 HTML 的時(shí)候并不保證會(huì)遵循這個(gè)策略腿椎,應(yīng)該 HTML 文檔的發(fā)起 是 沒(méi)有任何響應(yīng)頭來(lái)做參考的。所以夭咬,可以通過(guò) HTML 文檔中的 meta 標(biāo)簽來(lái)指定發(fā)送的請(qǐng)求頭
<meta name="referrer" content="no-referrer|no-referrer-when-downgrade|origin|origin-when-crossorigin|unsafe-url">
不過(guò)啃炸,上述的設(shè)置對(duì)于純粹從瀏覽器輸出網(wǎng)址發(fā)送 HTML 請(qǐng)求的情況都是無(wú)效的。其默認(rèn)都是 no-referrer-when-downgrade 策略卓舵。