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

伊利亞 (Ilya Grigorik)
Ilya Grigorik

透過網路擷取資源既緩慢,又所費不貲:

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

如何避免不必要的網路要求?瀏覽器的 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) 中嵌入檔案的指紋或版本號碼。

如要回應含有「指紋」或版本資訊,且其內容永遠不會改變的要求,請在回應中加入 Cache-Control: max-age=31536000

設定這個值會告知瀏覽器,如果在未來一年內隨時載入同一個網址 (31,536,000 秒,支援上限的值),可以直接使用 HTTP 快取中的值,而無需向網路伺服器發出網路要求。也就是說,您可以立即獲得避開網路所帶來的可靠性和速度!

Webpack 等建構工具可自動執行指派雜湊指紋給資產網址的程序

針對未版本網址重新驗證伺服器

不過,並非所有載入的網址都已建立版本。也許您無法在部署網頁應用程式之前加入建構步驟,因此無法將雜湊新增至資產網址。而且每個網頁應用程式都需要 HTML 檔案 (幾乎!這些檔案都不會加入版本管理功能),因為如果他人需要記住網址,就不用再使用網頁應用程式,https://example.com/index.34def12.html那你可以對這些網址進行什麼操作?

你必須承認敗場。光是 HTTP 快取功能不夠強大,無法完全避開網路。(別擔心,您不久後就會學到「Service Worker」,這將提供我們需要的支援,讓您將戰鬥擺脫並贏得您支持。)不過,您仍可採取幾個步驟,確保網路要求盡可能快速且有效率。

下列 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 回應,相當於「Ok,繼續使用目前擁有的內容!」傳送這類回應時,因為僅需要轉移少量資料,因此通常比必須實際回傳實際資源副本要來得快。

要求資源的用戶端和伺服器以 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 檔案) 會經常更新,而檔案的其餘部分 (例如程式庫程式碼) 不會更新,請考慮將經常更新的程式碼分割成個別檔案,並針對經常更新的程式碼使用短的時間快取策略,並針對不會經常變更的程式碼採用較長的快取時間策略。
  • 檢查新的 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 系統不允許快取回應,且每次要求時都必須完整擷取回應。