透過網路擷取資源既緩慢又昂貴:
- 大型回應需要在瀏覽器和伺服器之間進行多次往返。
- 必須等到所有重要資源都下載完畢,網頁才會載入。
- 如果使用者透過有限的行動數據方案存取網站,不必要的網路要求會浪費他們的金錢。
如何避免不必要的網路要求?瀏覽器的 HTTP 快取是第一道防線。這不一定是最強大或最靈活的方法,而且您對快取回應的生命週期控制有限,但這種方法很有效,所有瀏覽器都支援,而且不需要太多工作。
本指南將說明有效導入 HTTP 快取的基本概念。
瀏覽器相容性
實際上,並沒有名為 HTTP 快取的單一 API。這是一組網頁平台 API 的統稱。所有瀏覽器都支援這些 API:
Cache-Control
ETag
Last-Modified
HTTP 快取的運作方式
瀏覽器發出的所有 HTTP 要求都會先轉送至瀏覽器快取,檢查是否有有效的快取回應可用於滿足要求。如有相符項目,系統會從快取讀取回應,這樣就不會產生網路延遲和資料傳輸費用。
HTTP 快取的行為是由要求標頭和回應標頭共同控制。在理想情況下,您應能控制網頁應用程式的程式碼 (決定要求標頭) 和網路伺服器的設定 (決定回應標頭)。
如要進一步瞭解概念,請參閱 MDN 的 HTTP 快取一文。
要求標頭:通常使用預設值
網頁應用程式的傳出要求應包含多個重要標頭,但瀏覽器在提出要求時,幾乎一律會代為設定這些標頭。系統會根據瀏覽器對 HTTP 快取中目前值的解讀,顯示影響新鮮度檢查的要求標頭,例如 If-None-Match 和 If-Modified-Since。
這表示您可以在 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-age 或 expires 判定該版本不再是最新版本,或因其他原因 (例如使用者清除瀏覽器快取) 從快取中移除為止。因此,在建構網頁時,不同使用者可能會使用不同版本的檔案:剛擷取資源的使用者會使用新版本,而快取先前 (但仍有效) 副本的使用者則會使用舊版本的回應。
如何兼顧用戶端快取和快速更新?您變更資源的網址,並在內容變更時強制使用者下載新的回應。一般來說,您可以在檔案名稱中嵌入檔案指紋或版本號碼,例如 style.x234dff.css。
如果回應的網址含有「fingerprint」或版本資訊,且內容不應變更,請在回應中加入 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 範例。
設定 ETag 或 Last-Modified 也有幫助。如「回應標頭」一節所述,ETag 和 Last-Modified 的用途相同,都是判斷瀏覽器是否需要重新下載已過期的快取檔案。建議使用 ETag,因為這個方法更準確。
假設自初始擷取後已過了 120 秒,且瀏覽器已針對相同資源發出新要求。首先,瀏覽器會檢查 HTTP 快取,並找出先前的回應。很抱歉,瀏覽器無法使用先前的回應,因為回應已過期。此時,瀏覽器可以分派新要求,並擷取新的完整回應。不過,如果資源未變更,就沒有理由下載快取中已有的相同資訊,因此這種做法效率不彰!
ETag 標頭中指定的驗證符記,就是用來解決這個問題。伺服器會產生並傳回任意權杖,通常是檔案內容的雜湊或其他指紋。瀏覽器不需要知道指紋的產生方式,只要在下一個要求中將指紋傳送至伺服器即可。如果指紋仍相同,表示資源未變更,瀏覽器可以略過下載。
設定 ETag 或 Last-Modified,可觸發「要求標頭」一節中提到的 If-Modified-Since 或 If-None-Match 要求標頭,大幅提升重新驗證要求的效率。
如果網頁伺服器設定正確,在看到這些傳入的要求標頭時,就能確認瀏覽器在 HTTP 快取中的資源版本,是否與網頁伺服器上的最新版本相符。如果相符,伺服器就會傳回 304 Not Modified HTTP 回應,相當於「請繼續使用現有內容!」。傳送這類回應時,需要傳輸的資料量非常少,因此通常比實際傳回所要求資源的副本快得多。
/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:用於版本化資源。
ETag 或 Last-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 |
不允許快取回應,且每次要求都必須完整擷取。 |