HTTP 快取

HTTP 快取儲存與請求關聯的響應,並對後續請求重用儲存的響應。

可重用性有幾個優點。首先,由於無需將請求傳送到源伺服器,因此客戶端和快取越近,響應速度就越快。最典型的例子是瀏覽器本身為瀏覽器請求儲存快取。

此外,當響應可重用時,源伺服器無需處理請求——因此無需解析和路由請求、根據 cookie 恢復會話、查詢資料庫以獲取結果或渲染模板引擎。這減少了伺服器的負載。

快取的正常執行對系統的健康至關重要。

快取型別

HTTP 快取規範中,主要有兩種快取型別:私有快取共享快取

私有快取

私有快取是與特定客戶端繫結的快取——通常是瀏覽器快取。由於儲存的響應不與其他客戶端共享,私有快取可以為該使用者儲存個性化響應。

另一方面,如果個性化內容儲存在私有快取以外的快取中,那麼其他使用者可能會檢索這些內容——這可能會導致無意的資訊洩露。

如果響應包含個性化內容,並且您只想將響應儲存在私有快取中,則必須指定private指令。

http
Cache-Control: private

個性化內容通常由 cookie 控制,但 cookie 的存在並不總是表示它是私有的,因此僅憑 cookie 不能使響應成為私有的。

共享快取

共享快取位於客戶端和伺服器之間,可以儲存可在使用者之間共享的響應。共享快取可以進一步細分為代理快取託管快取

代理快取

除訪問控制功能外,一些代理還實現快取以減少網路流量。這通常不由服務開發人員管理,因此必須透過適當的 HTTP 標頭等進行控制。然而,過去,過時的代理快取實現——例如無法正確理解 HTTP 快取標準的實現——經常給開發人員帶來問題。

以下“萬能標頭”用於嘗試解決“舊的且未更新的代理快取”實現,這些實現不理解當前的 HTTP 快取規範指令,例如no-store

http
Cache-Control: no-store, no-cache, max-age=0, must-revalidate, proxy-revalidate

然而,近年來,隨著 HTTPS 變得越來越普遍,客戶端/伺服器通訊變得加密,路徑中的代理快取在許多情況下只能隧道傳輸響應,而不能充當快取。因此在這種情況下,無需擔心甚至無法看到響應的過時代理快取實現。

另一方面,如果 TLS 橋接代理透過在 PC 上安裝由組織管理的 CA(證書頒發機構)的證書,以中間人方式解密所有通訊,並執行訪問控制等操作——它可以看到響應的內容並快取它。然而,近年來 CT(證書透明度)已廣泛普及,並且某些瀏覽器僅允許帶有 SCT(簽名證書時間戳)的證書,因此此方法需要應用企業策略。在這種受控環境中,無需擔心代理快取“過時且未更新”。

託管快取

託管快取由服務開發人員明確部署,旨在減輕源伺服器的負載並高效地交付內容。示例包括反向代理、CDN 和與 Cache API 結合使用的 Service Worker。

託管快取的特性因部署的產品而異。在大多數情況下,您可以透過Cache-Control標頭和自己的配置檔案或儀表板控制快取的行為。

例如,HTTP 快取規範本質上沒有定義顯式刪除快取的方法——但透過託管快取,可以透過儀表板操作、API 呼叫、重啟等方式隨時刪除儲存的響應。這允許更主動的快取策略。

也可以忽略標準 HTTP 快取規範協議,轉而進行顯式操作。例如,可以指定以下內容以選擇退出私有快取或代理快取,同時使用您自己的策略僅在託管快取中進行快取。

http
Cache-Control: no-store

例如,Varnish Cache 使用 VCL(Varnish 配置語言,一種DSL)邏輯來處理快取儲存,而與 Cache API 結合使用的 Service Worker 允許您在 JavaScript 中建立該邏輯。

這意味著如果託管快取有意忽略no-store指令,則無需將其視為“不符合”標準。您應該做的是,避免使用萬能標頭,而是仔細閱讀您正在使用的任何託管快取機制的文件,並確保您以所選機制提供的方式正確控制快取。

請注意,某些 CDN 提供僅對該 CDN 有效的自己的標頭(例如,Surrogate-Control)。目前,正在努力定義一個CDN-Cache-Control標頭以使這些標準化。

Types of caches, including a private cache in the browser, a shared (proxy) cache, a reverse proxy cache, and a shared (managed) cache in a CDN, leading to the origin server's cache

啟發式快取

HTTP 旨在儘可能多地進行快取,因此即使未給出Cache-Control,如果滿足某些條件,響應也會被儲存和重用。這稱為啟發式快取

例如,看以下響應。此響應最後更新於 1 年前。

http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Last-Modified: Tue, 22 Feb 2021 22:22:22 GMT

<!doctype html>
…

根據經驗,已知一年未更新的內容在之後一段時間內也不會更新。因此,客戶端會儲存此響應(儘管缺少max-age)並重用一段時間。重用多長時間取決於實現,但規範建議儲存後大約為 10%(在本例中為 0.1 年)。

啟發式快取是在Cache-Control支援廣泛採用之前的一種權宜之計,基本上所有響應都應明確指定Cache-Control標頭。

基於年齡的新鮮度和陳舊度

儲存的 HTTP 響應有兩種狀態:新鮮陳舊新鮮狀態通常表示響應仍然有效且可以重用,而陳舊狀態表示快取的響應已過期。

確定響應何時新鮮何時陳舊的標準是年齡。在 HTTP 中,年齡是自響應生成以來經過的時間。這類似於其他快取機制中的TTL

看以下示例響應(604800 秒為一週)

http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Cache-Control: max-age=604800

<!doctype html>
…

儲存示例響應的快取計算自響應生成以來經過的時間,並將結果用作響應的年齡

對於示例響應,max-age的含義如下

  • 如果響應的年齡小於一週,則響應是新鮮的
  • 如果響應的年齡大於一週,則響應是陳舊的

只要儲存的響應保持新鮮,它將用於滿足客戶端請求。

當響應儲存在共享快取中時,可以告訴客戶端響應的年齡。繼續示例,如果共享快取儲存響應一天,共享快取將向後續客戶端請求傳送以下響應。

http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Cache-Control: max-age=604800
Age: 86400

<!doctype html>
…

接收該響應的客戶端將發現它在剩餘的 518400 秒內是新鮮的,這是響應的max-ageAge之間的差值。

Expires 或 max-age

在 HTTP/1.0 中,新鮮度曾經透過Expires標頭指定。

Expires標頭透過指定明確的時間而不是經過的時間來指定快取的生命週期。

http
Expires: Tue, 28 Feb 2022 22:22:22 GMT

然而,時間格式難以解析,發現了許多實現錯誤,並且可能透過故意移動系統時鐘引起問題;因此,HTTP/1.1 中在Cache-Control中採用了max-age——用於指定經過的時間。

如果ExpiresCache-Control: max-age都可用,則定義max-age為優先。因此,現在 HTTP/1.1 已廣泛使用,無需提供Expires

Vary

區分響應的方式本質上是基於它們的 URL

URL 響應體
https://example.com/index.html <!doctype html>...
https://example.com/style.css body { ...
https://example.com/script.js function main () { ...

但響應的內容並不總是相同的,即使它們具有相同的 URL。特別是當執行內容協商時,來自伺服器的響應可能取決於AcceptAccept-LanguageAccept-Encoding請求標頭的值。

例如,對於以Accept-Language: en標頭返回並快取的英語內容,不希望隨後將該快取的響應重用於具有Accept-Language: ja請求標頭的請求。在這種情況下,您可以透過將Accept-Language新增到Vary標頭的值中,使響應根據語言分別快取。

http
Vary: Accept-Language

這導致快取基於響應 URL 和Accept-Language請求標頭的組合進行鍵控,而不是僅僅基於響應 URL。

URL Accept-Language 響應體
https://example.com/index.html ja-JP <!doctype html>...
https://example.com/index.html en-US <!doctype html>...
https://example.com/style.css ja-JP body { ...
https://example.com/script.js ja-JP function main () { ...

此外,如果您根據使用者代理提供內容最佳化(例如,用於響應式設計),您可能會傾向於在Vary標頭的值中包含User-Agent。然而,User-Agent請求標頭通常具有非常大量的變體,這大大降低了快取重用的機會。因此,如果可能,請考慮一種基於功能檢測而不是基於User-Agent請求標頭來改變行為的方法。

對於使用 cookie 來防止其他人重用快取的個性化內容的應用程式,您應該指定Cache-Control: private而不是為Vary指定 cookie。

驗證

陳舊的響應不會立即丟棄。HTTP 有一種機制,透過向源伺服器詢問來將陳舊的響應轉換為新鮮的響應。這稱為驗證,有時也稱為重新驗證

驗證透過使用包含If-Modified-SinceIf-None-Match請求標頭的條件請求完成。

If-Modified-Since

以下響應在 22:22:22 生成,max-age為 1 小時,因此您知道它在 23:22:22 之前都是新鮮的。

http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Last-Modified: Tue, 22 Feb 2022 22:00:00 GMT
Cache-Control: max-age=3600

<!doctype html>
…

在 23:22:22,響應變為陳舊,快取無法重用。因此,下面的請求顯示客戶端傳送帶有If-Modified-Since請求標頭的請求,以詢問伺服器自指定時間以來是否進行了任何更改。

http
GET /index.html HTTP/1.1
Host: example.com
Accept: text/html
If-Modified-Since: Tue, 22 Feb 2022 22:00:00 GMT

如果內容自指定時間以來沒有更改,伺服器將響應304 Not Modified

由於此響應僅表示“未更改”,因此沒有響應體——只有一個狀態碼——因此傳輸大小極小。

http
HTTP/1.1 304 Not Modified
Content-Type: text/html
Date: Tue, 22 Feb 2022 23:22:22 GMT
Last-Modified: Tue, 22 Feb 2022 22:00:00 GMT
Cache-Control: max-age=3600

收到該響應後,客戶端將儲存的陳舊響應恢復為新鮮狀態,並可在剩餘的 1 小時內重用它。

伺服器可以從作業系統檔案系統獲取修改時間,這對於提供靜態檔案的情況來說相對容易。然而,存在一些問題;例如,時間格式複雜且難以解析,分散式伺服器難以同步檔案更新時間。

為了解決這些問題,ETag響應標頭被標準化為替代方案。

ETag/If-None-Match

ETag響應標頭的值是伺服器生成的任意值。伺服器如何生成該值沒有限制,因此伺服器可以自由地根據其選擇的任何方式設定該值——例如正文內容的雜湊值或版本號。

例如,如果ETag標頭使用雜湊值,並且index.html資源的雜湊值為33a64df5,則響應將如下所示

http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
ETag: "33a64df5"
Cache-Control: max-age=3600

<!doctype html>
…

如果該響應是陳舊的,客戶端將快取響應的ETag響應標頭的值,並將其放入If-None-Match請求標頭中,以詢問伺服器資源是否已修改

http
GET /index.html HTTP/1.1
Host: example.com
Accept: text/html
If-None-Match: "33a64df5"

如果伺服器確定請求資源的ETag標頭值與請求中的If-None-Match值相同,則伺服器將返回304 Not Modified

但如果伺服器確定請求的資源現在應該具有不同的ETag值,則伺服器將改為響應200 OK和資源的最新版本。

注意:RFC9110 建議伺服器在可能的情況下為200響應同時傳送ETagLast-Modified。在快取重新驗證期間,如果If-Modified-SinceIf-None-Match都存在,則If-None-Match對驗證器具有優先權。如果您只考慮快取,您可能會認為Last-Modified是不必要的。然而,Last-Modified不僅對快取有用;它是一個標準 HTTP 標頭,也被內容管理(CMS)系統用於顯示最後修改時間,被爬蟲用於調整爬取頻率,以及用於其他各種目的。因此,考慮到整個 HTTP 生態系統,最好同時提供ETagLast-Modified

強制重新驗證

如果您不希望響應被重用,而是希望始終從伺服器獲取最新內容,您可以使用no-cache指令來強制驗證。

透過在響應中新增Cache-Control: no-cache以及Last-ModifiedETag——如下所示——如果請求的資源已更新,客戶端將收到200 OK響應;否則,如果請求的資源未更新,將收到304 Not Modified響應。

http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Last-Modified: Tue, 22 Feb 2022 22:00:00 GMT
ETag: "deadbeef"
Cache-Control: no-cache

<!doctype html>
…

通常認為max-age=0must-revalidate的組合與no-cache具有相同的含義。

http
Cache-Control: max-age=0, must-revalidate

max-age=0表示響應立即陳舊,must-revalidate表示一旦陳舊,就必須在不重新驗證的情況下不重用——因此,結合起來,語義似乎與no-cache相同。

然而,max-age=0的這種用法是由於在 HTTP/1.1 之前許多實現無法處理no-cache指令的事實遺留下來的——因此為了解決這個限制,max-age=0被用作一種權宜之計。

但是現在符合 HTTP/1.1 的伺服器已廣泛部署,沒有理由再使用max-age=0must-revalidate的組合——您應該直接使用no-cache

不快取

no-cache指令不會阻止響應的儲存,而是阻止未經重新驗證的響應的重用。

如果您不希望響應儲存在任何快取中,請使用no-store

http
Cache-Control: no-store

然而,一般來說,實踐中“不快取”的要求等同於以下情況:

  • 出於隱私原因,不希望除特定客戶端之外的任何人儲存響應。
  • 希望始終提供最新資訊。
  • 不知道過時的實現會發生什麼。

在這種情況下,no-store並非總是最合適的指令。

以下部分將更詳細地探討這些情況。

不要與他人共享

如果包含個性化內容的響應意外地被快取的其他使用者看到,那將是問題。

在這種情況下,使用private指令將導致個性化響應僅儲存在特定客戶端中,而不會洩露給快取的任何其他使用者。

http
Cache-Control: private

在這種情況下,即使給定no-store,也必須給定private

每次提供最新內容

no-store指令會阻止響應被儲存,但不會刪除同一 URL 的任何已儲存響應。

換句話說,如果某個 URL 已儲存舊響應,返回no-store不會阻止舊響應被重用。

然而,no-cache指令將強制客戶端在重用任何儲存的響應之前傳送驗證請求。

http
Cache-Control: no-cache

如果伺服器不支援條件請求,您可以強制客戶端每次訪問伺服器並始終獲取帶有200 OK的最新響應。

處理過時的實現

作為解決忽略no-store的過時實現的權宜之計,您可能會看到使用以下“萬能標頭”。

http
Cache-Control: no-store, no-cache, max-age=0, must-revalidate, proxy-revalidate

建議使用no-cache作為處理此類過時實現的替代方案,如果從一開始就給定no-cache,則不會有問題,因為伺服器將始終接收請求。

如果您擔心共享快取,可以透過新增private來確保防止意外快取

http
Cache-Control: no-cache, private

no-store會失去什麼

您可能認為新增no-store是選擇退出快取的正確方法。

然而,不建議隨意授予no-store,因為您會失去 HTTP 和瀏覽器擁有的許多優勢,包括瀏覽器的回退/前進快取。

因此,為了獲得 Web 平臺的完整功能集的優勢,請優先使用no-cacheprivate的組合。

重新載入和強制重新載入

驗證可以對請求和響應執行。

重新載入強制重新載入操作是從瀏覽器端執行的驗證的常見示例。

重新載入

為了從視窗損壞中恢復或更新到資源的最新版本,瀏覽器為使用者提供了重新載入功能。

瀏覽器重新載入期間傳送的 HTTP 請求的簡化檢視如下所示

http
GET / HTTP/1.1
Host: example.com
Cache-Control: max-age=0
If-None-Match: "deadbeef"
If-Modified-Since: Tue, 22 Feb 2022 20:20:20 GMT

(來自 Chrome、Edge 和 Firefox 的請求看起來非常像上面;來自 Safari 的請求看起來會有點不同。)

請求中的max-age=0指令指定“重用年齡為 0 或更少的響應”——因此,實際上,中間儲存的響應不會被重用。

結果,請求透過If-None-MatchIf-Modified-Since進行驗證。

這種行為也在Fetch標準中定義,可以透過呼叫fetch()並將快取模式設定為no-cache在 JavaScript 中重現(請注意,reload在這種情況下不是正確的模式)

js
// Note: "reload" is not the right mode for a normal reload; "no-cache" is
fetch("/", { cache: "no-cache" });

強制重新載入

瀏覽器在重新載入期間使用max-age=0是為了向後相容——因為在 HTTP/1.1 之前,許多過時的實現不理解no-cache。但是現在在這種用例中no-cache是好的,並且強制重新載入是繞過快取響應的另一種方式。

瀏覽器強制重新載入期間的 HTTP 請求如下所示

http
GET / HTTP/1.1
Host: example.com
Pragma: no-cache
Cache-Control: no-cache

(來自 Chrome、Edge 和 Firefox 的請求看起來非常像上面;來自 Safari 的請求看起來會有點不同。)

由於這不是帶有no-cache的條件請求,因此您可以確保會從源伺服器獲取200 OK

這種行為也在Fetch標準中定義,可以透過呼叫fetch()並將快取模式設定為reload在 JavaScript 中重現(請注意,它不是force-reload

js
// Note: "reload" — rather than "no-cache" — is the right mode for a "force reload"
fetch("/", { cache: "reload" });

避免重新驗證

永不更改的內容應透過使用快取清除(即,在請求 URL 中包含版本號、雜湊值等)來賦予較長的max-age

然而,當用戶重新載入時,即使伺服器知道內容是不可變的,也會發送重新驗證請求。

為了防止這種情況,可以使用immutable指令明確指示不需要重新驗證,因為內容永不更改。

http
Cache-Control: max-age=31536000, immutable

這可以防止在重新載入期間進行不必要的重新驗證。

請注意,Chrome 已更改其實現,以便在重新載入子資源時不再執行重新驗證,而不是實現該指令。

刪除儲存的響應

無法刪除已儲存有較長max-age的中間伺服器上的響應。

想象一下,以下來自https://example.com/的響應已儲存。

http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Cache-Control: max-age=31536000

<!doctype html>
…

您可能希望在伺服器上過期後覆蓋該響應,但一旦響應儲存,伺服器就無能為力——因為由於快取,不再有請求到達伺服器。

規範中提到的一種方法是使用不安全的方法(例如POST)傳送相同 URL 的請求,但對於許多客戶端來說,這很難做到。

Clear-Site-Data: cache標頭和指令值可用於清除瀏覽器快取——但對中間快取沒有影響。否則,響應將保留在瀏覽器快取中,直到max-age過期,除非使用者手動執行重新載入、強制重新載入或清除歷史記錄操作。

快取減少了對伺服器的訪問,這意味著伺服器失去了對該 URL 的控制。如果伺服器不想失去對 URL 的控制——例如,在資源頻繁更新的情況下——您應該新增no-cache,以便伺服器將始終接收請求併發送預期的響應。

請求合併

共享快取主要位於源伺服器之前,旨在減少對源伺服器的流量。

因此,如果多個相同的請求同時到達共享快取,中間快取將代表自己向源伺服器轉發單個請求,然後可以重用結果以服務所有客戶端。這稱為請求合併

請求合併發生在請求同時到達時,因此即使在響應中給定max-age=0no-cache,它也會被重用。

如果響應針對特定使用者進行個性化設定,並且您不希望它在合併中共享,則應新增private指令

Request collapse shown as multiple clients sending GET requests and a cache consolidating them into one GET to the origin. The origin server responds with a 200 OK that the cache shares back to all clients.

常見快取模式

Cache-Control規範中有許多指令,可能難以理解所有這些指令。但大多數網站都可以透過少量模式的組合來覆蓋。

本節描述了設計快取的常見模式。

預設設定

如上所述,快取的預設行為(即,沒有Cache-Control的響應)不僅僅是“不快取”,而是根據所謂的“啟發式快取”進行隱式快取。

為了避免這種啟發式快取,最好明確地為所有響應提供預設的Cache-Control標頭。

為了確保預設情況下始終傳輸資源的最新版本,通常的做法是使預設的Cache-Control值包含no-cache

http
Cache-Control: no-cache

此外,如果服務實現 cookie 或其他登入方法,並且內容針對每個使用者進行個性化,則還必須給定private,以防止與其他使用者共享

http
Cache-Control: no-cache, private

快取清除

最適合快取的資源是內容永不更改的靜態不可變檔案。對於確實更改的資源,一種常見的最佳實踐是每次內容更改時更改 URL,以便可以更長時間地快取 URL 單元。

例如,考慮以下 HTML

html
<script src="bundle.js"></script>
<link rel="stylesheet" href="build.css" />
<body>
  hello
</body>

在現代 Web 開發中,JavaScript 和 CSS 資源會隨著開發的進展頻繁更新。此外,如果客戶端使用的 JavaScript 和 CSS 資源版本不同步,顯示將中斷。

因此,上面的 HTML 使得用max-age快取bundle.jsbuild.css變得困難。

因此,您可以使用包含基於版本號或雜湊值的可變部分的 URL 來提供 JavaScript 和 CSS。一些方法如下所示。

# version in filename
bundle.v123.js

# version in query
bundle.js?v=123

# hash in filename
bundle.YsAIAAAA-QG4G6kCMAMBAAAAAAAoK.js

# hash in query
bundle.js?v=YsAIAAAA-QG4G6kCMAMBAAAAAAAoK

由於快取根據 URL 區分資源,因此當資源更新時 URL 發生變化,快取將不會再次重用。

html
<script src="bundle.v123.js"></script>
<link rel="stylesheet" href="build.v123.css" />
<body>
  hello
</body>

透過這種設計,JavaScript 和 CSS 資源都可以長時間快取。那麼max-age應該設定為多長時間呢?QPACK 規範對此問題提供了答案。

QPACK是用於壓縮 HTTP 標頭欄位的標準,其中定義了常用欄位值的表。

以下是一些常用的快取標頭值。

36 cache-control max-age=0
37 cache-control max-age=604800
38 cache-control max-age=2592000
39 cache-control no-cache
40 cache-control no-store
41 cache-control public, max-age=31536000

如果選擇這些編號選項之一,在透過 HTTP3 傳輸時,可以將值壓縮為 1 位元組。

數字373841分別表示一週、一個月和一年。

由於快取會在儲存新條目時刪除舊條目,因此即使將max-age設定為 1 周,儲存的響應在一週後仍然存在的可能性也不高。因此,實際上,選擇哪一個並沒有太大區別。

請注意,數字41max-age最長(1 年),但帶有public

public值的作用是使響應即使在存在Authorization標頭的情況下也可以儲存。

注意:僅當需要儲存響應時Authorization標頭已設定時才應使用public指令。否則,它不是必需的,因為只要給出max-age,響應就會儲存在共享快取中。

因此,如果響應透過基本身份驗證進行個性化,則public的存在可能會導致問題。如果您擔心這一點,可以選擇第二長的值,38(1 個月)。

http
# response for bundle.v123.js

# If you never personalize responses via Authorization
Cache-Control: public, max-age=31536000

# If you can't be certain
Cache-Control: max-age=2592000

驗證

不要忘記設定Last-ModifiedETag標頭,這樣在重新載入時就不必重新傳輸資源。對於預構建的靜態檔案,生成這些標頭很容易。

這裡的ETag值可能是檔案的雜湊值。

http
# response for bundle.v123.js
Last-Modified: Tue, 22 Feb 2022 20:20:20 GMT
ETag: "YsAIAAAA-QG4G6kCMAMBAAAAAAAoK"

此外,可以新增immutable以防止在重新載入時進行驗證。

綜合結果如下所示。

http
# bundle.v123.js
HTTP/1.1 200 OK
Content-Type: text/javascript
Content-Length: 1024
Cache-Control: public, max-age=31536000, immutable
Last-Modified: Tue, 22 Feb 2022 20:20:20 GMT
ETag: "YsAIAAAA-QG4G6kCMAMBAAAAAAAoK"

快取清除是一種技術,透過在內容更改時更改 URL,使響應能夠長時間快取。該技術可以應用於所有子資源,例如影像。

注意:在評估immutable和 QPACK 的使用時:如果您擔心immutable會更改 QPACK 提供的預定義值,請考慮在這種情況下,immutable部分可以透過將Cache-Control值分成兩行來單獨編碼——儘管這取決於特定 QPACK 實現使用的編碼演算法。

http
Cache-Control: public, max-age=31536000
Cache-Control: immutable

主資源

與子資源不同,主資源無法進行快取清除,因為它們的 URL 無法像子資源 URL 那樣進行修飾。

如果以下 HTML 本身已儲存,即使內容在伺服器端更新,也無法顯示最新版本。

html
<script src="bundle.v123.js"></script>
<link rel="stylesheet" href="build.v123.css" />
<body>
  hello
</body>

在這種情況下,no-cache會更合適——而不是no-store——因為我們不想儲存 HTML,而只是希望它始終保持最新。

此外,新增Last-ModifiedETag將允許客戶端傳送條件請求,並且如果 HTML 沒有更新,則可以返回304 Not Modified

http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Cache-Control: no-cache
Last-Modified: Tue, 22 Feb 2022 20:20:20 GMT
ETag: "AAPuIbAOdvAGEETbgAAAAAAABAAE"

此設定適用於非個性化 HTML,但對於使用 cookie 進行個性化的響應(例如,登入後),請不要忘記同時指定private

http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Cache-Control: no-cache, private
Last-Modified: Tue, 22 Feb 2022 20:20:20 GMT
ETag: "AAPuIbAOdvAGEETbgAAAAAAABAAE"
Set-Cookie: __Host-SID=AHNtAyt3fvJrUL5g5tnGwER; Secure; Path=/; HttpOnly

同樣的設定也可以用於favicon.icomanifest.json.well-known和其 URL 無法透過快取清除更改的 API 端點。

大多數 Web 內容可以透過上述兩種模式的組合來覆蓋。

關於託管快取的更多資訊

使用前面部分描述的方法,子資源可以透過快取清除長時間快取,但主資源(通常是 HTML 文件)則不能。

快取主資源很困難,因為僅使用 HTTP 快取規範中的標準指令,無法在伺服器上內容更新時主動刪除快取內容。

但是,透過部署託管快取(例如 CDN 或 Service Worker)可以實現這一點。

例如,允許透過 API 或儀表板操作清除快取的 CDN 將允許更積極的快取策略,透過儲存主資源並在伺服器上發生更新時僅顯式清除相關快取。

如果 Service Worker 可以在伺服器上發生更新時刪除 Cache API 中的內容,它也可以這樣做。

有關更多資訊,請參閱 CDN 的文件,並查閱Service Worker 文件

另見