寫在前面:如下文章對筆者理解瀏覽器緩存有較大的幫助,感謝做者的分享。html
緩存分爲服務端側(server side,好比 Nginx、Apache)和客戶端側(client side,好比 web browser)。sql
服務端緩存又分爲代理服務器緩存和反向代理服務器緩存(也叫網關緩存,好比 Nginx反向代理、Squid等),其實普遍使用的 CDN 也是一種服務端緩存,目的都是讓用戶的請求走」捷徑「,而且都是緩存圖片、文件等靜態資源。瀏覽器
客戶端側緩存通常指的是瀏覽器緩存,目的就是加速各類靜態資源的訪問,想一想如今的大型網站,隨便一個頁面都是一兩百個請求,天天 pv 都是億級別,若是沒有緩存,用戶體驗會急劇降低、同時服務器壓力和網絡帶寬都面臨嚴重的考驗。緩存
Web緩存遊走於服務器和客戶端之間。這個服務器多是源服務器也多是代理服務器。服務器
Web緩存就在服務器-客戶端之間搞監控,監控請求,而且把請求輸出的內容(例如html頁面、 圖片和文件)(統稱爲副本)另存一份;網絡
而後,若是下一個請求是相同的URL,則直接請求保存的副本,而不是再次麻煩源服務器。負載均衡
不管對於網站運營者或者用戶,帶寬都表明着金錢,過多的帶寬消耗,只會便宜了網絡運營商。當Web緩存副本被使用時,只會產生極小的網絡流量,能夠有效的下降運營成本。分佈式
給網絡資源設定有效期以後,用戶能夠重複使用本地的緩存,減小對源服務器的請求,間接下降服務器的壓力。同時,搜索引擎的爬蟲機器人也能根據過時機制下降爬取的頻率,也能有效下降服務器的壓力。
帶寬對於我的網站運營者來講是十分重要,而對於大型的互聯網公司來講,可能有時由於錢多而真的不在意。那Web緩存還有做用嗎?答案是確定的,對於最終用戶,緩存的使用可以明顯加快頁面打開速度,達到更好的體驗。
瀏覽器緩存控制機制有兩種:HTML Meta標籤 vs. HTTP頭信息
HTTP頭緩存策略分爲兩種:強緩存和協商緩存。
實現強緩存能夠經過兩種響應頭實現:Expires 和 Cache-Control 。
協商緩存須要客戶端和服務端共同實現,和強緩存同樣,也有兩種實現方式。
瀏覽器緩存機制,其實主要就是HTTP協議定義的緩存機制(如: Expires; Cache-control等)。可是也有非HTTP協議定義的緩存機制,如使用HTML Meta 標籤,Web開發者能夠在HTML頁面的<head>節點中加入<meta>標籤,代碼以下:
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
上述代碼的做用是告訴瀏覽器當前頁面不被緩存,每次訪問都須要去服務器拉取。使用上很簡單,但只有部分瀏覽器能夠支持,並且全部緩存代理服務器都不支持,由於代理不解析HTML內容自己。而普遍應用的仍是 HTTP頭信息來控制緩存,下面我主要介紹HTTP協議定義的緩存機制。
瀏覽器第一次請求流程圖:
瀏覽器再次請求時:
Expires策略:Expires是Web服務器響應消息頭字段。
在響應http請求時告訴瀏覽器在過時時間前瀏覽器能夠直接從瀏覽器緩存取數據,而無需再次請求。
不過Expires 是HTTP 1.0的東西,如今默認瀏覽器均默認使用HTTP 1.1,因此它的做用基本忽略。
Expires 的一個缺點就是,返回的到期時間是服務器端的時間,這樣存在一個問題,若是客戶端的時間與服務器的時間相差很大(好比時鐘不一樣步,或者跨時區),那麼偏差就很大,因此在HTTP 1.1版開始,使用Cache-Control: max-age=秒替代。
Expires頭惟一的有效值是HTTP時間,其餘值都會被認爲是「前男朋友前女朋友」之類,不會去緩存的。注意:時間是格林威治時間(GMT),而不是本地時間。以下所示:
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Cache-control策略(重點關注):Cache-Control與Expires的做用一致,都是指明當前資源在瀏覽器緩存時間的有效期,控制瀏覽器是否直接從瀏覽器緩存取數據仍是從新發請求到服務器取數據。只不過Cache-Control的選擇更多,設置更細緻,若是同時設置的話,其優先級高於Expires。
Last-Modified/If-Modified-Since:Last-Modified/If-Modified-Since要配合Cache-Control使用。
Last-Modified:標示這個響應資源的最後修改時間。web服務器在響應請求時,告訴瀏覽器資源的最後修改時間。
I
f-Modified-Since:當資源過時時(使用
Cache-Control標識的max-age),發現資源具備Last-Modified聲明,
則再次向web服務器請求時帶上頭 If-Modified-Since,表示請求時間。web服務器收到請求後發現有頭If-Modified-Since 則與被請求資源的最後修改時間進行比對。
若最後修改時間較新,說明資源又被改動過,則響應整片資源內容(寫在響應消息包體內),HTTP 200;
若最後修改時間較舊,說明資源無新修改,則響應HTTP 304 (無需包體,節省流量),告知瀏覽器繼續使用所保存的cache。
Etag:web服務器響應請求時,告訴瀏覽器當前資源在服務器的惟一標識(生成規則由服務器決定)。
Apache中,ETag的值,默認是對文件的索引節(INode),大小(Size)和最後修改時間(MTime)進行Hash後獲得的。
If-None-Match:當資源過時時(使用Cache-Control標識的max-age),發現資源具備Etage聲明,則再次向web服務器請求時帶上頭If-None-Match (Etag的值)。
web服務器收到請求後發現有頭If-None-Match 則與被請求資源的相應校驗串進行比對,決定返回200或304。
你可能會以爲使用Last-Modified已經足以讓瀏覽器知道本地的緩存副本是否足夠新,爲何還須要Etag(實體標識)呢?
HTTP1.1中Etag的出現主要是爲了解決幾個Last-Modified比較難解決的問題:
Last-Modified標註的最後修改只能精確到秒級,若是某些文件在1秒鐘之內,被修改屢次的話,它將不能準確標註文件的修改時間。
若是某些文件會被按期生成,當有時內容並無任何變化,但Last-Modified卻改變了,致使文件無法使用緩存有可能存在服務器沒有準確獲取文件修改時間,或者與代理服務器時間不一致等情形
Etag是服務器自動生成或者由開發者生成的對應資源在服務器端的惟一標識符,可以更加準確的控制緩存。
Last-Modified與ETag一塊兒使用時,服務器會優先驗證ETag。
yahoo的Yslow法則中則提示謹慎設置Etag:須要注意的是分佈式系統裏多臺機器間文件的last-modified必須保持一致,以避免負載均衡到不一樣機器致使比對失敗,Yahoo建議分佈式系統儘可能關閉掉Etag(每臺機器生成的etag都會不同,由於除了 last-modified、inode 也很難保持一致)。
Pragma行是爲了兼容HTTP1.0,做用與Cache-Control: no-cache是同樣的。
最後總結下幾種狀態碼的區別:
瀏覽器緩存行爲還有用戶的行爲有關,若是你們對強制刷新(Ctrl + F5) 還有印象的話應該能馬上明白個人意思~
用戶操做 |
Expires/Cache-Control |
Last-Modified/Etag |
地址欄回車 |
有效 |
有效 |
頁面連接跳轉 |
有效 |
有效 |
新開窗口 |
有效 |
有效 |
前進、後退 |
有效 |
有效 |
F5/按鈕刷新 |
無效(BR重置max-age=0) |
有效 |
Ctrl+F5刷新 |
無效(重置CC=no-cache) |
無效(請求頭丟棄該選項)
|
指令 | 說明 |
---|---|
public | 全部內容都被緩存 |
private | 僅客戶端緩存代理服務器不緩存 |
no-cache | 必須先與代理服務器確認是否更改,而後在在決定使用緩存仍是請求 |
no-store | 全部內容都不會被緩存 |
must-revalidation/proxy-revalidation | 若是緩存內容失效,請求必須發送服務器/代理進行驗證 |
max-age=<s> | 緩存內容在s秒後失效,僅HTTP1.1可用 |
指令 | 打開新窗口 | 原窗口單擊Enter按鈕 | 刷新 | 點擊返回按鈕 |
public | 緩存頁面 | 緩存頁面 | 從新請求頁面 | 緩存頁面 |
private | 從新請求 | 第一次請求,隨後緩存 | 從新請求頁面 | 緩存頁面 |
no-cache/no-store | 從新請求 | 從新請求 | 從新請求 | 緩存頁面 |
must-revalidation/proxy-revalidation | 瀏覽器從新請求 | 第一次請求,隨後緩存 | 從新請求 | 緩存頁面 |
max-age=xxx | 在XX秒後從新請求 | 在XX秒後從新請求 | 從新請求 | 在xx秒後從新請求 |