原文連接: https://github.com/yiliang114...
這裏說的緩存是指瀏覽器(客戶端)在本地磁盤中對訪問過的資源保存的副本文件。css
瀏覽器緩存主要有如下幾個優勢:html
瀏覽器緩存分爲強緩存和協商緩存,二者有兩個比較明顯的區別:nginx
chrome
中強緩存(雖然沒有發出真實的 http
請求)的請求狀態碼返回是 200 (from cache)
;而協商緩存若是命中走緩存的話,請求的狀態碼是 304 (not modified)
。 不一樣瀏覽器的策略不一樣,在 Fire Fox
中,from cache
狀態碼是 304.其中 from cache 會分爲 from disk cache 和 from memory cache. 從內存中獲取最快,可是是 session 級別的緩存,關閉瀏覽器以後就沒有了。
![]()
瀏覽器在第一次請求後緩存資源,再次請求時,會進行下面兩個步驟:git
header
中的信息,根據 response header
中的 expires
和 cache-control
來判斷是否命中強緩存,若是命中則直接從緩存中獲取資源。IF-Modified-Since
或者 IF-None-Match
, 它們的值分別是第一次請求返回 Last-Modified
或者 Etag
,由服務器來對比這一對字段來判斷是否命中。若是命中,則服務器返回 304 狀態碼,而且不會返回資源內容,瀏覽器會直接從緩存獲取;不然服務器最終會返回資源的實際內容,並更新 header 中的相關緩存字段。借用網上的一張圖片github
強緩存是根據返回頭中的 Expires
或者 Cache-Control
兩個字段來控制的,都是表示資源的緩存有效時間。chrome
Expires
是 http 1.0
的規範,值是一個GMT
格式的時間點字符串,好比 Expires:Mon,18 Oct 2066 23:59:59 GMT
。這個時間點表明資源失效的時間,若是當前的時間戳在這個時間以前,則斷定命中緩存。有一個缺點是,失效時間是一個絕對時間,若是服務器時間與客戶端時間誤差較大時,就會致使緩存混亂。而服務器的時間跟用戶的實際時間是不同是很正常的,因此 Expires
在實際使用中會帶來一些麻煩。Cache-Control
這個字段是 http 1.1
的規範,通常經常使用該字段的 max-age
值來進行判斷,它是一個相對時間,好比 .Cache-Control:max-age=3600
表明資源的有效期是 3600 秒。而且返回頭中的 Date
表示消息發送的時間,表示當前資源在 Date ~ Date +3600s
這段時間裏都是有效的。不過我在實際使用中經常遇到設置了 max-age
以後,在 max-age
時間內從新訪問資源卻會返回 304 not modified
,這是因爲服務器的時間與本地的時間不一樣形成的。固然 Cache-Control
還有其餘幾個值能夠設置, 不過相對來講都不多用了:後端
no-cache
不使用本地緩存。須要使用協商緩存。no-store
直接禁止瀏覽器緩存數據,每次請求資源都會向服務器要完整的資源, 相似於 network
中的 disabled cache
。public
能夠被全部用戶緩存,包括終端用戶和 cdn 等中間件代理服務器。private
只能被終端用戶的瀏覽器緩存。若是 Cache-Control
與 Expires
同時存在的話, Cache-Control
的優先級高於 Expires
。瀏覽器
協商緩存是由服務器來肯定緩存資源是否可用。 主要涉及到兩對屬性字段,都是成對出現的,即第一次請求的響應頭帶上某個字, Last-Modified
或者 Etag
,則後續請求則會帶上對應的請求字段 If-Modified-Since
或者 If-None-Match
,若響應頭沒有 Last-Modified
或者 Etag
字段,則請求頭也不會有對應的字段。緩存
Last-Modified/If-Modified-Since
兩者的值都是 GMT 格式的時間字符串, Last-Modified
標記最後文件修改時間, 下一次請求時,請求頭中會帶上 If-Modified-Since
值就是 Last-Modified
告訴服務器我本地緩存的文件最後修改的時間,在服務器上根據文件的最後修改時間判斷資源是否有變化, 若是文件沒有變動則返回 304 Not Modified
,請求不會返回資源內容,瀏覽器直接使用本地緩存。當服務器返回 304 Not Modified
的響應時,response header
中不會再添加的 Last-Modified
去試圖更新本地緩存的 Last-Modified
, 由於既然資源沒有變化,那麼 Last-Modified
也就不會改變;若是資源有變化,就正常返回返回資源內容,新的 Last-Modified
會在 response header
返回,並在下次請求以前更新本地緩存的 Last-Modified
,下次請求時,If-Modified-Since
會啓用更新後的 Last-Modified
。Etag/If-None-Match
, 值都是由服務器爲每個資源生成的惟一標識串,只要資源有變化就這個值就會改變。服務器根據文件自己算出一個哈希值並經過 ETag
字段返回給瀏覽器,接收到 If-None-Match
字段之後,服務器經過比較二者是否一致來斷定文件內容是否被改變。與 Last-Modified
不同的是,當服務器返回 304 Not Modified
的響應時,因爲在服務器上ETag
從新計算過,response header
中還會把這個 ETag
返回,即便這個 ETag
跟以前的沒有變化。HTTP 中並無指定如何生成 ETag,能夠由開發者自行生成,哈希是比較理想的選擇。
HTTP1.1
中 Etag
的出現主要是爲了解決幾個 Last-Modified
比較難解決的問題:服務器
If-Modified-Since
能檢查到的粒度是秒級的,使用 Etag
就可以保證這種需求下客戶端在 1 秒內能刷新 N 次 cache。Cache-Control > expires > Etag > Last-Modified
簡單說就是 F5 刷新的時候,會暫時禁用強緩存
通過對 qq、fire fox 、safari 、chrome 這幾個瀏覽器的訪問同一個頁面測試我發現,不一樣的瀏覽器在 F5 刷新的時候 ,同一個文件 qq 、fire fox 瀏覽器會返回 304 Not Nodified
,在請求頭中不攜帶 Expires/Cache-Control
; 而 chrome 和 safari 刷新的時候,會返回 200 from cache
, 沒有真正發起請求,走強緩存。可見不一樣的瀏覽器反饋是不一致的,因此下面表格中"F5 刷新"時 Expires/Cache-Control
會無效我認爲是存在必定爭議的。
而 Ctrl + F5 強制刷新的時候,會暫時禁用強緩存和協商緩存。
在寫這篇博客時,對於我僅僅測試了一個瀏覽器以後便寫了無效(由於網上大多數帖子寫了無效,我也覺得我驗證經過了),對指出這個問題的羣友,表示感謝,但願其餘人不會被我誤導。
用戶操做 | Expires/Cache-Control | Last-Modied/Etag |
---|---|---|
地址欄回車 | 有效 | 有效 |
頁面連接跳轉 | 有效 | 有效 |
新開窗口 | 有效 | 有效 |
前進回退 | 有效 | 有效 |
F5 刷新 | 無效(有爭議,不一樣瀏覽器反饋不一致) | 有效 |
Ctrl+F5 強制刷新 | 無效 | 無效 |
後端服務器,寫入代碼邏輯中:
res.setHeader('max-age': '3600 public') res.setHeader(etag: '5c20abbd-e2e8') res.setHeader('last-modified': Mon, 24 Dec 2018 09:49:49 GMT)
Nginx
配置
add_header Cache-Control "max-age=3600"
通常來講,經過 nginx 靜態資源服務器,會默認給資源帶上強緩存、協商緩存的 header 字段。
cache-control
定義的 max-age
時間以內,js
, css
文件會走強緩存,http
狀態碼是 200, 跟服務器也並不會有交互。可是第一個文件 index.html
文件, 每次回車或者刷新都是狀態碼都是 304 ,由於它的請求頭中默認每次都攜帶了 Cache-Control: max-age=0
。js
css
文件 cache-control
超時以後,從新按回車會走協商緩存,請求服務器發現資源沒有改變,因而返回 304 ,瀏覽器從緩存中獲取內容,從 size
中也能夠看出端倪, 幾百 B 的包不是靜態資源的體積。最後總結一下瀏覽器的三級緩存原理: