http頭字段緩存詳解(1)


1,瀏覽器緩存機制:

現在的瀏覽器都有緩存機制,一般使用F5刷新頁面都是返回瀏覽器緩存的數據,
如果瀏覽頁面發現異常時,使用Ctrl+F5組合鍵重新請求的頁面就是沒有緩存的最新頁面

F5刷新:

ctrl+f5刷新:

1.1,為什么使用Ctrl+F5重新請求的就是沒有緩存的頁面呢?

當使用Ctrl+F5組合鍵刷新頁面時

ps:緩存方式分為瀏覽器和服務器緩存。

1.2,原理:

當我們在使用Ctrl+F5組合鍵刷新一個頁面時,
會在HTTP請求頭中增加一些請求項(最重要的是在請求頭中增加了兩個請求項Pragma:no-cache和Cache-Control:no-cache)
通過它來告訴服務器我們要獲取的是最新的數據,而不是緩存數據

Cache-Control / Pragma 這個HTTP Head字段用於指定所有緩存機制在整個請求/響應鏈中必須服從的指令。

1.3,Cache-Control字段有一些可選值,如下所示:

Public :所有內容都將被緩存,在響應頭中設置
Private :內容只緩存到私有服務器中,在響應頭中設置
no-cache :所有內容都不會被緩存,在請求頭和響應頭中設置
另一種說法:不是不緩存而是使用緩存需要驗證(可以通過請求頭If-None-Match和響應頭ETag,來對緩存的有效性進行驗證。)
(ps:應該是可選擇的配合驗證,而不是必須配套使用)

no
-store :所有內容都不會被緩存到緩存或Internet臨時文件中,在響應頭中設置 must-revalidation/proxy-revalidation :如果緩存的內容失效,請求必須發送到服務器/代理以進行重新驗證,在請求頭中設置 max-age=xxx :緩存的內容將在xxx秒后失效,這個選項只在HTTP1.1中可用,和Last-Modified一起使用時優先級較高,在響應頭中設置

1.4,HTTP其他緩存頭字段:

Expires :

它通常的使用格式是Expires:Fri ,24 Dec 2027 04:24:07 GMT,后面跟的是日期和時間,
超過這個時間后,緩存的內容將失效,瀏覽器在發出請求之前會先檢查這個頁面的這個字段,查看頁面是否已經過期,過期了就重新向服務器發起請求;

Last-Modified / If-Modified:

它一般用於表示一個服務器上的資源最后的修改時間,資源可以是靜態或動態的內容,
通過這個最后修改時間可以判斷當前請求的資源是否是最新的。
一般服務端在響應頭中返回一個Last-Modified字段告訴瀏覽器這個頁面的最后修改時間
瀏覽器再次請求時會在請求頭中增加一個If-Modified字段詢問當前緩存的頁面是否是最新的
如果是最新的就返回304狀態碼,告訴瀏覽器是最新的,服務器也不會傳輸新的數據
ps:也就是服務器省略了具體內容的傳輸,只需返回一個標識狀態碼

補充:304狀態碼

304(未修改)    
自從上次請求后,請求的網頁未修改過。服務器返回此響應時,不會返回網頁內容。

Etag/If-None-Match:

(ps:一般用於當Cache-Control:no-cache時,用於驗證緩存有效性)

Etag與Last-Modified有類似的功能,它的作用是讓服務端給每個頁面分配一個唯一 的編號,然后通過這個編號來區分當前這個頁面是否是最新的,
這種方式更加靈活,但是后端如果有多台Web服務器時不太好處理,因為每個Web服務器都要記住網站的所有資源,否則瀏覽器返回這個編號就沒有意義了。

疑問:哪些是瀏覽器主動控制的,哪些是服務器主動控制的。

 PS:總結:

為什么響應頭可能幾種參數同時存在:
Cache-Control:max-age=315360000
ETag:"96a3-558a6b1f51e40"
Expires:Tue, 20 Jun 2028 06:17:28 GMT
Last-Modified:Fri, 08 Sep 2017 05:17:37 GMT

個人理解是:
1,是存在優先級
2,是瀏覽器兼容問題
即真正起作用的只是其中某個某幾個參數。

2,詳解Cache-Control和ETag(請求頭If-None-Match、響應頭ETag)

請求頭中和響應頭中的Cache-Control的區別

請求頭里的Cache-Control是no-cache,是瀏覽器通知服務器:本地沒有緩存數據
響應頭中的 Cache-Control:max-age=259200 是通知瀏覽器:259200 秒之內別來煩我,自己從緩沖區中刷新
如果響應頭中有 Cache-Control=no-cache 那么瀏覽器是不會緩存的
不過即使有緩存,也不一定被瀏覽器使用。因為瀏覽器還有其他設置
同時並不是與緩存相關的頭都會被瀏覽器接受,協議只是建議,並不一個定非要執行
請求頭里的no-cache表示瀏覽器不想讀緩存,並不是說沒有緩存。
一般在瀏覽器按ctrl+F5強制刷新時,請求頭里就有這個no-cache,也就是跳過強緩存和協商緩存階段,直接請求服務器。
(如果直接按F5的話,請求頭是max-age=0,只跳過強緩存,但進行協商緩存)

關注請求頭If-None-Match、響應頭ETag、響應頭Cache-Control

當今絕大多數的瀏覽器,都支持這三個Http頭。
我們所要做的就是,確保每個服務器響應都提供正確的 HTTP 頭指令,以指導瀏覽器何時可以緩存響應以及可以緩存多久。

緩存在哪兒?

上圖中有三個角色,瀏覽器、Web代理和服務器,如圖所示Http緩存存在於瀏覽器和Web代理中。
當然在服務器內部,也存在着各種緩存,但這已經不是本文要討論的Http緩存了。
所謂的Http緩存控制,就是一種約定,通過設置不同的響應頭Cache-Control來控制瀏覽器和Web代理對緩存的使用策略,
通過設置請求頭If-None-Match和響應頭ETag,來對緩存的有效性進行驗證

響應頭ETag

ETag全稱Entity Tag,用來標識一個資源。
在具體的實現中,ETag可以是資源的hash值,也可以是一個內部維護的版本號。
但不管怎樣,ETag應該能反映出資源內容的變化,這是Http緩存可以正常工作的基礎。

如上例中所展示的,服務器在返回響應時,通常會在Http頭中包含一些關於響應的元數據信息,
其中,ETag就是其中一個,本例中返回了值為x1323ddx的ETag。
當資源
/file的內容發生變化時,服務器應當返回不同的ETag。

請求頭If-None-Match

對於同一個資源,比如上一例中的/file,在進行了一次請求之后,瀏覽器就已經有了/file的一個版本的內容,和這個版本的ETag,
當下次用戶再需要這個資源,瀏覽器再次向服務器請求的時候,可以利用請求頭If-None-Match來告訴服務器自己已經有個ETag為x1323ddx的/file,
這樣,如果服務器上的/file沒有變化,也就是說服務器上的/file的ETag也是x1323ddx的話,服務器就不會再返回/file的內容,
而是返回一個304的響應,告訴瀏覽器該資源沒有變化,緩存有效。

響應頭Cache-Control

每個資源都可以通過Http頭Cache-Control來定義自己的緩存策略,
Cache-Control控制誰在什么條件下可以緩存響應以及可以緩存多久。
最快的請求是不必與服務器進行通信的請求:
通過響應的本地副本,我們可以避免所有的網絡延遲以及數據傳輸的數據成本。
為此,HTTP 規范允許服務器返回一系列不同的 Cache-Control 指令,控制瀏覽器或者其他中繼緩存如何緩存某個響應以及緩存多長時間。
Cache-Control 頭在 HTTP/1.1 規范中定義,取代了之前用來定義響應緩存策略的頭(例如 Expires)。
當前的所有瀏覽器都支持 Cache-Control,因此,使用它就夠了。

Cache-Control中設置的常用指令

max-age
該指令指定從當前請求開始,允許獲取的響應被重用的最長時間(單位為秒。
例如:Cache
-Control:max-age=60表示響應可以再緩存和重用 60 秒。
需要注意的是,在max-age指定的時間之內,瀏覽器不會向服務器發送任何請求,包括驗證緩存是否有效的請求
也就是說,如果在這段時間之內,服務器上的資源發生了變化,那么瀏覽器將不能得到通知,而使用老版本的資源。
所以在設置緩存時間的長度時,需要慎重。 public和private 如果設置了public表示該響應可以再瀏覽器或者任何中繼的Web代理中緩存public是默認值
即Cache
-Control:max-age=60等同於Cache-Control:public, max-age=60。 在服務器設置了private比如Cache-Control:private, max-age=60的情況下,
表示只有用戶的瀏覽器可以緩存private響應,不允許任何中繼Web代理對其進行緩存
例如,用戶瀏覽器可以緩存包含用戶私人信息的 HTML 網頁,但是 CDN 不能緩存。
no
-cache 如果服務器在響應中設置了no-cache即Cache-Control:no-cache
那么瀏覽器在使用緩存的資源之前必須先與服務器確認返回的響應是否被更改,如果資源未被更改,可以避免下載。
這個驗證之前的響應是否被修改,就是通過上面介紹的請求頭If-None-match和響應頭ETag來實現的需要注意的是,no-cache這個名字有一點誤導。
設置了no-cache之后,並不是說瀏覽器就不再緩存數據,只是瀏覽器在使用緩存數據時,需要先確認一下數據是否還跟服務器保持一致。
如果設置了no-cache,而ETag的實現沒有反應出資源的變化,那就會導致瀏覽器的緩存數據一直得不到更新的情況。 no-store 如果服務器在響應中設置了no-store即Cache-Control:no-store
那么瀏覽器和任何中繼的Web代理,都不會存儲這次相應的數據
當下次請求該資源時,瀏覽器只能重新請求服務器,重新從服務器讀取資源。

怎樣決定一個資源的Cache-Control策略呢?

3,HTTP緩存機制[譯文]

通過重用已獲取的資源,可大幅提高web站點和應用的性能。
由於web緩存減少了延遲和網絡流量,因此縮短了展示一個資源所需的時間。
通過使用HTTP緩存機制,web站點可實現更快更靈活的響應。

緩存的目的:

1,減輕服務器壓力(服務器不用每次為所有客戶端提供服務了),
2,提高訪問效率(因為緩存離客戶端最近,可直接提供資源副本,也可節省很多傳輸時間)。

所有不同類型的緩存,大致可以歸為兩類:

私有緩存 和 共享緩存。
1,共享緩存中存儲的資源副本是供所有用戶使用的(比如不同瀏覽器,不同機器),
2,私有緩存是僅提供給單個用戶的專有緩存(不同用戶保留不同私有緩存副本)。
本文僅討論瀏覽器緩存和代理緩存,但就目前來講,還有很多其他類型的緩存,
比如:網關緩存、CDN、反向代理緩存、負載均衡
(負載均衡是部署在服務器端的,為多個web服務器提供更可靠、更高性能以及更易進行規模化擴展的方案)。

瀏覽器(私有)緩存

私有緩存是單個用戶的專有緩存,
一般來講,在你的瀏覽器設置中就可以看到“緩存”的選項。
瀏覽器緩存保留了用戶通過HTTP下載的所有文檔資源,前進/后退、保存、查看源代碼等操作都可以使用到此緩存,而不用再重新訪問服務器。
同樣的,有了緩存,我們還可以實現脫機瀏覽文檔和資源。

代理(共享)緩存

共享緩存中存放的訪問結果是提供給多個用戶使用的。
比如:ISP或你的公司可能會組建一個本地網絡的代理,該代理(服務器)會緩存不同用戶訪問外網時請求的公共資源,
這些公共資源被緩存后,下次其他用戶也訪問同一資源時,就會重用此已被緩存的資源(就不用再向源站獲取了),從而減少了網絡瀏覽和延遲。

緩存操作的目標

HTTP緩存通常只緩存GET請求(其他請求一般不緩存),緩存的主鍵由請求方法和目標URI(通常只用到URI,因為一般僅緩存GET請求)組成。

通常的緩存條目有:

成功的查詢請求的結果數據:狀態碼為200的GET響應(結果中可能包含資源數據如:HTML文檔、圖片或文件等)
永久性跳轉:狀態碼為301(Moved Permanently)的響應
返回出錯,文檔不存在:狀態碼為404(Not Found)的響應
不完整的結果數據:狀態碼為206(Partial Content)的響應(通過Range頭發起的請求所返回的結果,Range用於只獲取文檔某一部分)
其他非GET請求的結果(如果這些結果比較適合作為緩存的話)

緩存控制

Cache-control頭部

HTTP/1.1中,Cache-Control頭用於指定緩存機制中的不同指令,它是可用在請求報文及響應報文中的通用頭部。
通過該頭部提供的不同指令,你可以定義一個自己的緩存策略。

禁止緩存 方式

如下頭部定義,在該方式下,緩存不會保存任何的客戶端請求和服務器響應。
每次客戶端的請求都會發送到源服務器,並且每次源服務器返回的數據都會全部下載到客戶端。 Cache
-Control: no-store Cache-Control: no-cache, no-store, must-revalidate

強制確認緩存 方式

如下頭部定義,此方式下,每次有請求發出時,緩存會將此請求發到服務器(譯者注:該請求應該會帶有與本地緩存相關的驗證字段),
服務器端會驗證請求中所描述的緩存是否過期,若未過期(譯者注:實際就是返回304),則緩存才使用本地緩存副本。 Cache
-Control: no-cache

私有和公共緩存

"public" 指令表示該響應可以被任何中間人(譯者注:比如中間代理、CDN等)緩存。
若指定了"public",則一些通常不被中間人緩存的頁面
(比如 帶有HTTP驗證信息(帳號密碼)的頁面 或 某些特定影響狀態碼的頁面),將會被其緩存。 而
"private" 則表示該響應是專用於某單個用戶的,中間人不能緩存此響應,該響應只能應用於瀏覽器私有緩存中。 Cache-Control: private Cache-Control: public

過期機制

過期機制中,最重要的指令是 "max-age=<seconds>",表示資源能夠被緩存(保持新鮮)的最大時間。
與 Expires指令不同,該指令的值是相對於請求的那個時間之后的秒數。
對於那些不會變動的文檔資源,你可以直接將其設置為永久緩存,比如像圖片、CSS文件、JS文件這些靜態資源。
Cache-Control: max-age=31536000

驗證確認

當使用了 "must-revalidate" 指令,那就意味着緩存在考慮使用一個陳舊的資源時,必須先驗證它的狀態,並且,已過期的緩存將不被使用。
Cache-Control: must-revalidate

Pragma 頭部

Pragma 是HTTP/1.0規范中的頭部,它已經不是可靠的用於過期控制的頭部了,
盡管它的行為和 Cache-Control: no-cache 一致(未設置Cache-Control頭部的情況下)。
Pragma 現僅用於兼容 HTTP/1.0 客戶端。

新鮮度

理論上來講,當一個資源被緩存存儲后,該資源應該可以被永久存儲在緩存中。
由於緩存只有有限的空間用於存儲資源副本,所以緩存會定期地將一些副本刪除,這個過程叫做 緩存驅逐。
另一方面,當服務器上面的資源進行了更新,那么緩存中的對應資源也應該被更新,
由於HTTP是C/S模式的協議,服務器更新一個資源時,不可能直接通知客戶端及其緩存,
所以雙方必須為該資源約定一個過期時間,在該過期時間之前,該資源(緩存副本)就是 新鮮的,
當過了過期時間后,該資源(緩存副本)則變為 陳舊的。

驅逐算法用於將陳舊的資源(緩存副本)替換為新鮮的,注意,一個陳舊的資源(緩存副本)是不會直接被清除或忽略的,
當客戶端發起一個請求時,緩存檢索到已有一個對應的陳舊資源(緩存副本),則緩存會先將此請求附加一個If-None-Match頭,
然后發給目標服務器,以此來檢查該資源副本是否是依然還是算新鮮的,
若服務器返回了 304 (Not Modified)(該響應不會有帶有實體信息),則表示此資源副本是新鮮的,這樣一來,可以節省一些帶寬。
(譯者注:若服務器通過 If-None-Match 或 If-Modified-Since判斷后發現已過期,那么會帶有該資源的實體內容返回)
新鮮度的生命周期是通過若干頭部值來計算的,如果設置了 "Cache-control: max-age=N" 頭部,那么新鮮度的生命期則等於 N。
經常情況下,可能未設置此頭部,則會檢查 Expires 頭部是否存在,
若 Expires 頭部存在,則新鮮度生命期 等於 該頭部的值 減去 Date 頭部的值。
若兩種頭部都未設置,則會查找 Last-Modified 頭部,
若存在,則新鮮度生命期 等於 Date 頭部值 減去 Last-modified 頭部值 再除以 10

資源版本化

緩存使用越頻繁(譯者注:跟命中率有關了),那么網站的響應速度和效率就越高,為此,在最佳實踐中,我們推薦盡可能地將過期時間設置得長一些,
但這會導致我們很難去更新那些不常變動的資源。

比如我們經常會遇到這樣的需求:
很多頁面都引用了一些JS和CSS文件,當這些文件的內容變動時,我們希望能盡快地讓其在緩存中更新。
web開發者們研究出一個方案,Steve Sounders稱它為 revving[1]。
其原理是,將那些經常更新的文件的文件名通過一種特別的方式來命名,
即文件名中加入版本號,這樣一來,每一次文件內容改變,文件名也被一起改變,就相當於新建了另一個不同的資源,
那我們就可以將該資源設置為永久不過期了(通常設置為1年以上)。
但為了引用這個改動后的資源,所以鏈接到此資源的鏈接地址都需要改變(譯者注:其文件名改變后,相對應的URI也改變,所以鏈接到此資源的地址也應該改變),
這也是該方案的缺點:
帶來了額外的復雜性,通常web開發者會使用一些工具來自動應對此缺點(譯者注:如webpack),
當不常變動的資源改變時,這些資源的文件名URI也隨之改變,而引用這些資源的另外的經常變動的資源,也將隨之改變(引用地址改變),
當客戶端請求常變動的資源時,它里面引用的不常變動的資源,由於加入了版本號,所以其新的版本也被下載。
這種方案有一個好處:
可以解決2個資源過期時間不一致導致不一同被更新的問題。
這在當網站的CSS或JS資源擁有共同的依賴時顯得尤為重要,
比如他們引用了同一個HTML元素,導致他們互相依賴。
(譯者注:舉例,比如當前緩存了兩個JS文件A,B,A里面是比較早期的功能,A先過期,B是晚於A開發的功能,且依賴於A,B后過期,
假如這期間服務器端A和B都做了改變,加了新的功能,那么當A過期時拿到了最新版的A,而B還未過期,則使用的是舊版的B,這樣頁面運行時,可能導致嚴重錯誤)。
添加在資源名稱上面的版本號並非必須是像1.1.3這樣的常用版本定義方式,甚至可以僅僅是一個連續遞增的數字,
只要版本號不沖突,它可以是任意的,比如hash值或日期時間。

緩存的驗證確認

當用戶點擊刷新(重新加載)按鈕時,就會觸發一個再次驗證確認,當用戶正常訪問某網站/資源時,瀏覽器會檢查該請求對應的緩存內容,
若之前緩存的響應內容中,包含了 "Cache-control: must-revalidate" 頭部,那么也會觸發一個再次驗證確認。
另外一個引發再次驗證確認的因素是:用戶可以在瀏覽器的 Advanced->Cache 設置選項面板中設置 強制每次加載頁面時都進行驗證確認。 當一個緩存副本已經到了過期時間,那么就會先到服務器驗證確認此緩存的新鮮度,或者直接從服務器獲取該資源最新的內容。
驗證確認操作僅僅在服務器的響應信息中提供了 強驗證器 或 弱驗證器 才會發生。

ETag頭部

響應報文中的 ETag 頭是一個對 用戶代理透明 的值,被用來作為強驗證器,
意思就是說,像瀏覽器這樣的用戶代理程序並不知道其值代表的含義。
若在服務器的響應報文中含有 ETag 頭部,
則后續客戶端發起同一請求時,會附加一個 If-None-Match 頭部(其值為之前Etag的值)用於讓服務器驗證該請求對應的緩存是否新鮮。 響應報文中的 Last-Modified 頭用來作為弱驗證器,之所以作為“弱”,是因為它最多只能精確到秒,
若服務器的響應報文中存在 Last-Modified 頭部,那么客戶端發起一個 If-Modified-Since 請求來驗證緩存是否新鮮。 當發起了一個驗證請求,服務器可以忽略此驗證請求並返回一個正常的 200 OK 響應報文,
或者也可以返回 304 Not Modified(帶有一個空的實體)來告知瀏覽器:
你可以使用當前緩存副本,后者(304)的響應報文中也可以附加一些頭部,用來更新當前緩存副本中緩存的頭部信息。

可變響應 - Vary頭部

響應報文中的 Vary 頭部用於 決定如何匹配后續請求的頭部,從而決定是否采用某緩存副本,而不是從服務器獲得一個新的副本。

當發起一個請求時,緩存命中了一個副本(之前緩存的響應報文信息),
而這個副本中含有 Vary 頭部,那么緩存需要檢查 Vary 頭部中所列出的頭部字段,
若在當前請求中的這些頭部字段值 與 緩存副本中響應的頭部字段值匹配,
那么可以使用此緩存副本,否則需要不能使用此副本(重新請求服務器)。
有了這個頭部,就可以動態地提供內容了,
舉個例子,
當我們使用 Vary: User-Agent 頭部時,緩存服務器就需要去查看新發起的請求的 User-Agent 頭部是否匹配,
然后再決定是否采用緩存。如果你需要為手機端用戶展示不同的內容,
此舉可以防止將電腦端的緩存錯誤地提供給了手機端的用戶,
並且還可以幫助Google這些搜索引擎發現並抓取手機端版本的頁面,以及告訴搜索引擎這不是 Cloaking 作弊。 Vary: User
-Agent 由於 User-Agent 頭部在移動端和電腦端中的值都是不同的,所以緩存就不會錯誤地將移動端內容提供給電腦端用戶了,反之亦然。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM