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

Ilya Grigorik
Ilya Grigorik

透過網路擷取資源不但速度慢,成本也很高:

  • 大型回應需要瀏覽器與伺服器之間進行多次往返作業。
  • 網頁的所有重要資源下載完畢後,才會載入。
  • 如果使用者以有限的行動數據方案存取您的網站,每一個不必要的網路要求都會浪費他們的金錢。

如何避免不必要的網路要求?瀏覽器的 HTTP 快取是您的第一道防線。這不一定是最強大或最有彈性的方法,而且您對快取回應的生命週期有限,但這個方法有效,而且適用於所有瀏覽器,而且不需要進行大量操作。

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

瀏覽器相容性

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

Cache-Control

瀏覽器支援

  • 12

來源

ETag

瀏覽器支援

  • 12

來源

Last-Modified

瀏覽器支援

  • 12

來源

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 檔案一年 (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,使用者就不用再使用您的網頁應用程式。那麼 Google 對這些網址有什麼幫助?

也就是您必須承受擊敗的那種情境。單靠 HTTP 快取功能不夠強大,無法完全避開網路。(請放心,您很快就會瞭解服務工作人員,這些工作人員會提供所需支援,幫助我們放鬆,對您有利)。不過,您可以採取幾個步驟,確保網路要求的速度和效率都有所提升。

下列 Cache-Control 值可協助您微調快取網址的快取位置和方式:

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

請參閱附錄:Cache-Control流程圖,以視覺化的方式呈現決定使用哪個 Cache-Control 值的程序。Cache-Control 也能接受以半形逗號分隔的指令清單。請參閱附錄:Cache-Control 範例

設定 ETagLast-Modified 也有幫助。如回應標頭中所述,ETagLast-Modified 都具有相同的用途,也就是判斷瀏覽器是否需要重新下載已過期的快取檔案。建議您使用 ETag,因為這個方法更為準確。

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 的「快取最佳做法和 max-age 作業」指南。

請參閱「快取快取」,瞭解如何針對回訪者最佳化快取使用方式。

附錄:更多訣竅

如果你有更多時間,可以透過下列方法最佳化 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 系統不允許快取回應,且每次要求都必須完整擷取。