使用 HTTP 快取,避免不必要的網路要求

Ilya Grigorik
Ilya Grigorik

透過網路擷取資源既緩慢又耗費資源:

  • 大型回應需要在瀏覽器和伺服器之間進行多次來回傳輸。
  • 所有重要資源都必須完全下載完畢,網頁才會載入。
  • 如果使用者是透過行動數據方案有限的方案存取您的網站,每項不必要的網路要求都會浪費使用者的金錢。

如何避免不必要的網路要求?瀏覽器的 HTTP 快取是第一道防線。這不一定是最強大或最具彈性的做法,而且您無法完全控制快取回應的生命週期,但這項做法相當有效,且所有瀏覽器都支援,而且不需要太多工作。

本指南將說明有效 HTTP 快取實作方式的基本概念。

瀏覽器相容性

事實上,並沒有一個名為 HTTP 快取的 API。這是一系列網路平台 API 的一般名稱。所有瀏覽器都支援下列 API:

Cache-Control

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Source

ETag

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Source

Last-Modified

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Source

HTTP 快取的運作方式

瀏覽器發出的所有 HTTP 要求都會先傳送至瀏覽器快取,以便檢查是否有可用來滿足要求的有效快取回應。如果找到相符項目,系統就會從快取中讀取回應,這樣一來,系統就不會產生網路延遲和資料移轉費用。

HTTP 快取的行為是由要求標頭回應標頭共同控制。在理想情況下,您可以控制網頁應用程式的程式碼 (用於決定要求標頭) 和網頁伺服器設定 (用於決定回應標頭)。

如需更深入的概念總覽,請參閱 MDN 的「HTTP 快取」一文。

要求標頭:(通常) 使用預設值

網頁應用程式的傳出要求中應包含許多重要標頭,但瀏覽器在提出要求時,幾乎都會代為設定這些標頭。影響新鮮度檢查的請求標頭 (例如 If-None-MatchIf-Modified-Since) 會根據瀏覽器對 HTTP 快取中目前值的理解顯示。

這項功能可讓您在 HTML 中繼續加入 <img src="my-image.png"> 等標記,瀏覽器會自動處理 HTTP 快取,無須額外操作。

回應標頭:設定網路伺服器

HTTP 快取設定中最關鍵的部分,就是網路伺服器在每個傳出回應時新增的標頭。下列標頭都會影響有效快取行為:

  • Cache-Control。伺服器可以傳回 Cache-Control 指令,指定瀏覽器和其他中介快取應如何快取個別回應,以及快取多久。
  • ETag。當瀏覽器發現快取回應已過期時,可以傳送一個小型符記 (通常是檔案內容的雜湊值) 給伺服器,以便檢查檔案是否已變更。如果伺服器傳回相同的權杖,則表示檔案相同,因此無須重新下載。
  • Last-Modified:這個標頭與 ETag 的用途相同,但會使用以時間為準的策略來判斷資源是否已變更,而非 ETag 的以內容為準策略。

部分網頁伺服器預設會內建支援設定這些標頭,但有些網頁伺服器則會完全略過標頭,除非您明確設定。如何設定標頭的具體細節,會因您使用的網頁伺服器而有極大差異,因此請參閱伺服器說明文件,取得最準確的詳細資訊。

為了節省您的搜尋時間,以下提供幾個常見網路伺服器的設定說明:

省略 Cache-Control 回應標頭不會停用 HTTP 快取!相反地,瀏覽器會有效地猜測哪種快取行為最適合特定類型的內容。您可能會想要進一步控管回應標頭,因此請花點時間設定回應標頭。

您應該使用哪些回應標頭值?

設定網路伺服器的回應標頭時,您必須考量兩種重要情境。

針對版本化網址進行長效快取

假設您的伺服器會指示瀏覽器快取 CSS 檔案 1 年 (Cache-Control: max-age=31536000),但設計師剛剛進行了緊急更新,您需要立即部署。如何通知瀏覽器更新檔案的「過時」快取副本?您無法,至少需要變更資源的網址。

瀏覽器快取回應後,系統會使用快取版本,直到 max-ageexpires 判定該版本已過時,或直到該版本因其他原因 (例如使用者清除瀏覽器快取) 而從快取中移除為止。因此,在建構網頁時,不同使用者可能會使用不同版本的檔案:剛擷取資源的使用者會使用新版本,而快取較舊 (但仍有效) 副本的使用者則會使用舊版回應。

如何同時享有兩種優勢:用戶端快取和快速更新?您可以變更資源的網址,並強制使用者在內容變更時下載新的回應。通常,您可以透過在檔案名稱中嵌入檔案指紋或版本編號來達成這點,例如 style.x234dff.css

當您回應含有「指紋」或版本資訊的網址要求,且該網址內容永遠不會變更時,請在回應中加入 Cache-Control: max-age=31536000

設定這個值可讓瀏覽器知道,如果在未來一年內 (31,536,000 秒;這是支援的最大值) 需要載入相同的網址,可以立即使用 HTTP 快取中的值,完全不必向網路伺服器提出網路要求。太好了!你現在可以立即獲得避開網路帶來的可靠性和速度!

webpack 等建構工具可自動化為資產網址指派雜湊指紋。

針對未版本化的網址進行伺服器重新驗證

很抱歉,並非所有載入的網址都有版本。您可能無法在部署網路應用程式前加入建構步驟,因此無法在資產網址中加入雜湊。此外,每個網路應用程式都需要 HTML 檔案,而這些檔案 (幾乎) 不會包含版本資訊,因為如果使用者需要記住要造訪的網址是 https://example.com/index.34def12.html,就不會想使用網路應用程式。那麼,你能為這些網址做些什麼呢?

這是你需要認輸的情況之一。單靠 HTTP 快取功能無法完全避免網路連線。(別擔心,您很快就會學習服務工作者,這項技術可提供必要支援,讓您在戰鬥中取得優勢。)不過,您可以採取幾個步驟,確保網路要求盡可能快速且有效率。

您可以使用下列 Cache-Control 值,微調無版本號網址的快取位置和方式:

  • no-cache。這會指示瀏覽器,每次使用快取版本的網址前,都必須向伺服器重新驗證。
  • no-store。這會指示瀏覽器和其他中繼快取 (例如 CDN) 永遠不要儲存任何檔案版本。
  • private。瀏覽器可以快取檔案,但中介快取無法。
  • public。回應可由任何快取儲存。

請參閱附錄:Cache-Control流程圖,瞭解如何決定要使用的 Cache-Control 值。Cache-Control 也能接受以半形逗號分隔的指令清單。請參閱附錄:Cache-Control 範例

設定 ETagLast-Modified 也能解決問題。如回應標頭所述,ETagLast-Modified 的用途相同:判斷瀏覽器是否需要重新下載已到期的快取檔案。我們建議使用 ETag,因為這個值較為準確。

假設自初始擷取作業開始後,已過了 120 秒,且瀏覽器已針對相同資源發出新要求。首先,瀏覽器會檢查 HTTP 快取,並找出先前的回應。很抱歉,由於回應已過期,瀏覽器無法使用先前的回應。此時,瀏覽器可以調度新要求,並擷取新的完整回應。不過,這麼做效率不高,因為如果資源未變更,就沒有理由下載快取中已有的相同資訊!

這就是驗證權杖 (如 ETag 標頭中所述) 的設計目的。伺服器會產生並傳回任意權杖,通常為檔案內容的雜湊或其他指紋。瀏覽器不需要瞭解如何產生指紋,只需要在下一次要求中將指紋傳送至伺服器即可。如果指紋仍相同,表示資源並未變更,瀏覽器可以略過下載作業。

設定 ETagLast-Modified,讓重新驗證要求觸發 If-Modified-SinceIf-None-Match 要求標頭 (如「要求標頭」一節所述),可讓重新驗證要求更有效率。

當正確設定的網路伺服器看到這些傳入的要求標頭時,就能確認瀏覽器在 HTTP 快取中已有的資源版本,是否與網路伺服器上的最新版本相符。如果有相符項目,伺服器可以傳回 304 Not Modified HTTP 回應,這相當於「嘿,請繼續使用你目前的內容!」傳送這類回應時,需要傳輸的資料量很少,因此通常比實際傳回所要求的實際資源副本快上許多。

用戶端要求資源,而伺服器以 304 標頭回應的視覺化效果。
瀏覽器會向伺服器要求 /file,並加入 If-None-Match 標頭,指示伺服器只在檔案的 ETag 與瀏覽器的 If-None-Match 值不相符時,才傳回完整檔案。在這種情況下,2 個值確實相符,因此伺服器會傳回 304 Not Modified 回應,並提供檔案應快取多久的時間 (Cache-Control: max-age=120) 的操作說明。

摘要

HTTP 快取可減少不必要的網路要求,因此是提高載入效能的有效方法。所有瀏覽器都支援這項功能,而且設定起來也不費工。

以下是 Cache-Control 的初始設定:

  • Cache-Control: no-cache:適用於每次使用前都應透過伺服器重新驗證的資源。
  • Cache-Control: no-store 適用於不應快取的資源。
  • Cache-Control: max-age=31536000 代表版本資源。

ETagLast-Modified 標頭可協助您更有效率地重新驗證已過期的快取資源。

瞭解詳情

如果您想進一步瞭解如何使用 Cache-Control 標頭,請參閱 Jake Archibald 的快取最佳做法和最大年齡陷阱指南。

請參閱「愛上快取」一文,瞭解如何針對回訪訪客改善快取使用情形。

附錄:更多提示

如果您有更多時間,可以進一步調整 HTTP 快取的使用方式:

  • 使用一致的網址。如果您在不同網址上提供相同的內容,系統就會多次擷取及儲存該內容。
  • 盡量減少流失。如果資源的部分內容 (例如 CSS 檔案) 經常更新,但檔案的其他部分 (例如程式庫程式碼) 則不經常更新,建議將經常更新的程式碼分割到個別檔案,並針對經常更新的程式碼使用短暫快取策略,針對不經常變更的程式碼則使用長時間快取策略。
  • 如果 Cache-Control 政策可接受某種程度的過時性,請查看新的 stale-while-revalidate 指令。

附錄:Cache-Control 流程圖

流程圖
設定 Cache-Control 標頭的決策程序。

附錄:Cache-Control 範例

Cache-Control 說明
max-age=86400 瀏覽器和中介快取可以將回應快取最多 1 天 (60 秒 x 60 分鐘 x 24 小時)。
private, max-age=600 瀏覽器 (但不是中介快取) 最多可將回應快取 10 分鐘 (60 秒 x 10 分鐘)。
public, max-age=31536000 回應可由任何快取儲存 1 年。
no-store 系統不允許快取回應,且必須在每次要求時完整擷取。