原文: http://cncc.bingj.com/cache.aspx?q=max-age+expires+Last-Modified&d=4997458151473641&mkt=en-US&setlang=en-US&w=obLC-DXYkHDglJJNfr5xl1yTc4LW9a_o
本文著重論述了關於 HTTP 快取頭部的重要資訊,以及有關的 CDN 行為。假如你正在尋找現代 web 中 HTTP cache headers 的深入資訊,這裡有你所需的一切。
HTTP 快取頭部
主要通過新鮮度和有效性,來決定對資源的快取。如果一個有效的資源沒有改變過,很少會再次傳送完整的資源,此時快取中的一個新鮮的副本是立即可用的。假設沒有校驗器(如 ETag/Last-modified 頭部),並且缺少明確的新鮮度資訊的話,那就要經常(但並不總是)被認定為無法快取了。現在把焦點轉移到需要被考慮到的幾種 HTTP 頭部上:
1. Cache-control
- 每個資源都可以通過 Cache-Control HTTP 頭部來定義其快取策略
- Cache-Control 指令控制了誰、在什麼條件下來快取響應,以及快取多久
不需要伺服器通訊的請求被認為是最佳的請求:使用響應的本地副本,既可以消除網路延遲,又能避免資料傳輸帶來的網路負載。HTTP 規範允許伺服器傳送多個不同的 Cache-Control 指令,用以控制身處諸如CDN的中間人快取中,瀏覽器如何(以及何時)快取個別的響應:
Cache-control: private, max-age=0, no-cache複製程式碼
這些設定項被稱為響應指令,如下所示:
Public vs. Private
被標記為 `public` 的響應,即便在其關聯了一個 HTTP 認證或其 HTTP 響應狀態碼通常不可快取的情況下,也都可以被快取。在大多數情況下,標記 `public` 不是必要的,因為明確的快取資訊(如 `max-age`)已經表示響應是可快取的。
反之,被標記為`private`的響應,可以被(瀏覽器)快取,但是這樣的響應是典型地面向單個使用者的,因此不能被中間人快取(比如含有使用者私人資訊的 HTML 頁面可以被使用者的瀏覽器快取而非 CDN)。
No-cache 和 No-store
`no-cache`表示在檢查過伺服器響應是否已經改變之前,返回的響應不能被用於隨後向同一 URL 的請求。如果一個適當的 Etag (校驗記號) 作為一個結果出現,`no-cache`就會引發一次往返動作以試圖校驗已快取過的響應。如果資源未改變過,快取就會避免下載行為。換句話說,web 瀏覽器可能會快取資源,但不得不在每次請求都檢查資源是否已改變(未改變則返回 304)。
與之相反的是,`no-store`更簡單些。這樣說是因為它禁止瀏覽器和所有中間人從返回的響應中快取任何版本的資源,例如響應中包含隱私/個人的資訊或銀行資料等。使用者每次請求這個資源都是請求伺服器,每次資源都要被下載。
max-age
`max-age` 指令表示獲取到的資源被允許重複使用的最長時間,以秒為單位(從請求發起的時刻算起)。舉例來說,`max-age=90`指示了資源在之後的 90 秒鐘可被重用(儲存在瀏覽器快取中)。
s-maxage
`s-`代表`shard cache`。這個指令是明確針對中間人快取中的 CDN 的。當這個指令出現在頭部中時,會覆蓋掉 `max-age` 和 `expires` 的設定。
`Cache-Control` 是作為 HTTP/1.1 標準的一部分定義的,用來接替之前的頭部資訊(如 `expires`)來規定響應的快取策略。 `Cache-Control` 被所有現代瀏覽器支援,實乃我們必備的。
2. Pragma
舊的 `pragma` 頭部承擔了許多工作,其中大部分都有了更新的實現。我們一般將cache-control: no-cache
視為pragma: no-cache
的更新一些的實現。在整體的理解上了解這個指令是有必要的,但今後不會再為其定義新的 HTTP 指令了。
3. Expires
幾年前,這是指定資源有效期的主要途徑。Expires 只是個基本的 date-time 時間戳,對那些遊走在不規範地帶的使用者代理來說,還是相當管用的;而對於現代系統,則是優先使用cache-control
頭部,設定max-age
和s-maxage
等。為了相容的目的,在此設定匹配的值是個好的做法;同樣重要的是確保日期的格式正確,不然會被認為過期。
Expires: Sun, 03 May 2015 23:02:37 GMT複製程式碼
為了避免破壞規範,不要設定超過一年的日期值。(譯註:規範中的說明為 -- To mark a response as "never expires," an origin server sends an Expires date approximately one year from the time the response is sent. HTTP/1.1 servers SHOULD NOT send Expires dates more than one year in the future. )
4. Validators
ETag
這種在 HTTP/1.1 中定義的有效性 token:
- 通過伺服器的 `ETag` HTTP頭部傳達
- 確保資源更新的有效性,也就是說,如果資源沒改變,就不會發生資料傳輸
這個例子將展示其作用:在首次獲取一個資源 90 秒後,發起一次新的瀏覽器請求(完全一樣的資源);瀏覽器尋找本地快取,當找到上一次快取的請求後並發現其過期時,就會從伺服器請求完整的內容 -- 問題是,如果資源沒有改變過,在其已存在於 CDN 快取的情況下,絕無理由再下載一遍。
有效性 token 正是用來解決此類問題。邊緣伺服器(edge server, 譯註:如專門負責快取、防火牆、負載均衡等的第一層伺服器;其後是提供web服務的第二層和提供資料庫的第三層)建立並返回特製的 token,存放在 ETag 頭部域中,作為既有檔案的指紋資訊,一般用一個 hash 值來表示。客戶端不需要知道該 token 如何產生,只肖在隨後的請求中攜帶之便可。如果 token 相同,也就意味著資源沒變,這樣一來也就跳過了重新下載。
web 瀏覽器自動提供了“If-None-Match” 請求頭,用來包含 ETag token;伺服器將根據此 token 比對快取中的當前資源。如果快取中的資源未發生變化,瀏覽器將收到一個 `304 Not Modified` 的響應,有效性續期 90 秒。特別是因為不用重新下載資源,頻寬和時間都被節省了。
web 開發者如何從重新生效中獲益?
瀏覽器為 web 開發者承擔了大多數工作。比如,瀏覽器自動檢測到前一次指定的有效性 token ,將其附加到隨後的請求中,並按需基於伺服器響應更新快取的時間戳。web 開發者因此只需要確保伺服器提供所需的 ETag token 就行了。
Last-Modified
`Last-Modified` 頭部作為一個常見的校驗器,指示了檔案最後一次改變的時間。可以將其視為一個 HTTP/1.0 時代遺留的校驗器。當快取儲存了一個包含此頭部的資源時,可以利用其查詢伺服器資源是否已超時(從資源上次被使用時)。相應的請求頭部為 `If-Modified-Since`。
一個 HTTP/1.1 的源伺服器應該同時傳送 ETag 和 Last-Modified。更多細節可以在 RFC2616 規範中找到。
一個例子:
HTTP/1.1 200 OK
Server: keycdn-engine
Date: Mon, 27 Apr 2015 18:54:37 GMT
Content-Type: text/css
Content-Length: 44660
Connection: keep-alive
Vary: Accept-Encoding
Last-Modified: Mon, 08 Dec 2014 19:23:51 GMT
ETag: "5485fac7-ae74"
Cache-Control: max-age=533280
Expires: Sun, 03 May 2015 23:02:37 GMT
X-Cache: HIT
X-Edge-Location: defr
Access-Control-Allow-Origin: *
Accept-Ranges: bytes複製程式碼
TL;DR
Cache-Control 和 ETag 頭部域是用來控制資源新鮮度和有效性的現代機制。其他值則“僅僅”用來向後相容。
----------------------------------------