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

Ilya Grigorik
波斯尼克 (Jeff Posnick)
Jeff Posnick

透過網路擷取資源既緩慢又昂貴:

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

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

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

瀏覽器相容性

實際上,沒有任何一個稱為 HTTP 快取的 API。是一組網路平台 API 的通用名稱。所有瀏覽器均支援這些 API:

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) 中嵌入檔案的指紋或版本號碼。

回應包含「fingerprint」(指紋) 或版本資訊,且內容絕不會變更的要求時,請在回應中加入 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,因為其準確度較高。

ETag 範例

假設自初始擷取以來,120 秒已過,且瀏覽器已針對同一個資源發出新的要求。首先,瀏覽器會檢查 HTTP 快取,並找出先前的回應。很抱歉,由於回應已過期,瀏覽器無法使用先前的回應。此時,瀏覽器可以分派新的要求,並擷取新的完整回應。不過,由於資源並未變更,所以效率較低,所以沒有理由下載快取中現有的相同資訊!這就是驗證權杖 (如 ETag 標頭所指定) 旨在解決的問題。伺服器會產生並傳回任意權杖,通常是檔案內容的雜湊或其他指紋。瀏覽器不必知道指紋的產生方式,只需在下次要求時將指紋傳送至伺服器。如果指紋維持不變,表示資源並未變更,瀏覽器可以略過下載程序。

設定 ETagLast-Modified 可讓您更有效率地提出重新驗證要求。而會觸發要求標頭中提及的 If-Modified-SinceIf-None-Match 要求標頭。

當正確設定的網頁伺服器偵測到傳入的要求標頭時,可以確認瀏覽器的 HTTP 快取中既有的資源版本是否與網路伺服器的最新版本相符。如果比對相符,伺服器就會回應 304 Not Modified HTTP 回應,這相當於「Hey,繼續使用您已擁有的項目!」傳送這類回應時要傳輸的資料很少,因此通常比實際傳回實際要求資源副本的速度要快上許多。

用戶端要求資源,以及伺服器以 304 標頭回應。
瀏覽器向伺服器要求 /file,並附上 If-None-Match 標頭,指示伺服器只在伺服器上檔案的 ETag 與瀏覽器的 If-None-Match 值不符時,才傳回完整檔案。在這種情況下,這兩個值相符,因此伺服器會傳回 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 說明
max-age=86400 瀏覽器和中介快取可快取回應,最長可達 1 天 (60 秒 x 60 分鐘 x 24 小時)。
private, max-age=600 瀏覽器可快取回應,但不得快取中繼快取,最長可達 10 分鐘 (60 秒 x 10 分鐘)。
public, max-age=31536000 回應可以儲存在任何快取中 1 年。
no-store 系統不允許快取回應,因此必須在每次要求時完整擷取回應。