使用 Early Hints 伺服器思考時間,加快網頁載入速度

瞭解伺服器如何向瀏覽器傳送關於重要子資源的提示。

Kenji Baheux
Kenji Baheux

什麼是「早期提示」?

網站變得越來越複雜。因此,伺服器需要執行非簡單的工作 (例如存取資料庫,或 CDN 存取來源伺服器),以便產生要求網頁的 HTML,這並非不尋常。不幸的是,這段「伺服器思考時間」會導致瀏覽器在開始轉譯網頁前,產生額外的延遲時間。事實上,只要伺服器準備回應的時間過長,連線就會處於閒置狀態。

這張圖片顯示,在載入網頁和其他資源之間,伺服器的思考時間差距為 200 毫秒。
不使用早期提示:伺服器會封鎖所有內容,以決定如何回應主要資源。

早期提示是一種 HTTP 狀態碼 (103 Early Hints),用於在最終回應之前傳送初步 HTTP 回應。這樣一來,伺服器在忙於產生主要資源時,就能向瀏覽器傳送關於重要子資源 (例如網頁的樣式表單、重要 JavaScript) 或網頁可能會使用的來源的提示。瀏覽器可以使用這些提示來暖機連線,並在等待主要資源時要求子資源。換句話說,早期提示會提前執行一些工作,藉此加快網頁載入速度,從而協助瀏覽器善用這類「伺服器思考時間」。

這張圖片顯示早期提示如何讓網頁傳送部分回應。
透過早期提示:伺服器可發出含有資源提示的部分回應,同時決定最終回應

根據 ShopifyCloudflare 觀察到的結果,在某些情況下,Largest Contentful Paint 的效能改善幅度可能長達數百毫秒,而快上一秒,如下圖所示:

兩個網站的比較。
使用 WebPageTest (Moto G4 - DSL) 完成的測試網站,比較早期提示的前後差異

如何使用早期提示

要充分運用早期提示,第一步就是找出最熱門的到達網頁,也就是使用者造訪網站時通常會先看到的網頁。這可能會是首頁或熱門產品資訊頁面,如果您有許多使用者來自其他網站,這些進入點比其他網頁更重要,是因為使用者在網站中瀏覽時,早期提示的效用會降低 (也就是說,瀏覽器在第二或第三次後續導覽時,更有可能擁有所需的所有子資源)。建立良好的第一印象也是不錯的做法!

有了這個優先順序的到達網頁清單後,下一步就是找出哪些來源或子資源適合用於 preconnectpreload 提示。通常,這些來源和子資源會對最大內容繪製首次顯示內容所需時間等主要使用者指標貢獻最多。具體來說,請找出會造成轉譯阻斷的子資源,例如同步 JavaScript、樣式表,甚至是網路字型。同樣地,請找出代管子資源的來源,這些子資源對主要使用者指標貢獻良多。

另外請注意,如果主要資源已使用 preconnectpreload,您可以將這些來源或資源列為提早提示的候選項目。詳情請參閱如何改善 LCP。不過,單純將 preconnectpreload 指令從 HTML 複製到早期提示可能不是最佳選擇

在 HTML 中使用這些元素時,您通常會想 preconnectpreload 預先載入掃描器不會在 HTML 中發現的資源,例如字型或背景圖片,因為這些資源會在較晚的時間才會被發現。對於早期提示,您不會有 HTML,因此建議您改為 preconnect 至重要網域,或 preload重要資源,這些資源可能會在 HTML 中提早發現,例如預先載入 main.cssapp.js。此外,並非所有瀏覽器都支援早期提示的 preload,請參閱瀏覽器支援

第二步是盡可能降低在主要資源可能已過時或不再使用的資源或來源上使用早期提示的風險。舉例來說,經常更新且有版本的資源 (例如 example.com/css/main.fa231e9c.css) 可能不是最佳選擇。請注意,這個問題並非僅適用於 Early Hints,而是適用於任何 preloadpreconnect 的任何來源。這類細節最適合透過自動化或模板處理 (例如,手動程序較有可能導致 preload 與使用該資源的實際 HTML 標記之間的雜湊或版本網址不相符)。

舉例來說,請思考以下流程:

GET /main.html
Host: example.com
User-Agent: [....] Chrome/103.0.0.0 [...]

伺服器會預測需要 main.abcd100.css,並建議使用 Early Hints 預先載入:

103 Early Hints
Link: </main.abcd100.css>; rel=preload; as=style
[...]

稍待片刻,系統就會放送網頁,包括已連結的 CSS。很抱歉,這個 CSS 資源經常更新,而且主資源已比預測的 CSS 資源 (abcd100) 提前五個版本 (abcd105)。

200 OK
[...]
<HTML>
<head>
   <title>Example</title>
   <link rel="stylesheet" href="/main.abcd105.css">

一般來說,請選擇相當穩定的資源和來源,且與主要資源的結果無關。如有需要,您可以考慮將主要資源分成兩部分:一個穩定的部分可搭配 Early Hints 使用,另一個較具動態性的部分則可在瀏覽器收到主要資源後擷取:

<HTML>
<head>
   <title>Example</title>
   <link rel="stylesheet" href="/main.css">
   <link rel="stylesheet" href="/experimental.3eab3290.css">

最後,在伺服器端尋找已知支援早期提示的瀏覽器傳送的主要資源要求,並立即傳回 103 早期提示。在 103 回應中,加入相關的預先連線和預先載入提示。主要資源準備就緒後,請提供一般的回應 (例如:如果成功則為 200 OK)。為顧及回溯相容性,建議您在最終回應中加入 Link HTTP 標頭,甚至使用產生主要資源時明顯可見的重要資源擴充 (例如,如果您遵循「以兩個分割」建議,則金鑰資源的動態部分)。程式碼看起來會像這樣:

GET /main.html
Host: example.com
User-Agent: [....] Chrome/103.0.0.0 [...]
103 Early Hints
Link: <https://fonts.google.com>; rel=preconnect
Link: </main.css>; rel=preload; as=style
Link: </common.js>; rel=preload; as=script

幾分鐘後:

200 OK
Content-Length: 7531
Content-Type: text/html; charset=UTF-8
Content-encoding: br
Link: <https://fonts.google.com>; rel=preconnect
Link: </main.css>; rel=preload; as=style
Link: </common.js>; rel=preload; as=script
Link: </experimental.3eab3290.css>; rel=preload; as=style
<HTML>
<head>
   <title>Example</title>
   <link rel="stylesheet" href="/main.css">
   <link rel="stylesheet" href="/experimental.3eab3290.css">
   <script src="/common.js"></script>
   <link rel="preconnect" href="https://fonts.googleapis.com">

瀏覽器支援

雖然所有主要瀏覽器都支援 103 Early Hints,但可透過 Early Hints 傳送的指令會因瀏覽器而異:

Preconnect 支援:

瀏覽器支援

  • Chrome:103。
  • Edge:103。
  • Firefox:120。
  • Safari:17。

預先載入支援:

瀏覽器支援

  • Chrome:103。
  • Edge:103。
  • Firefox:123。
  • Safari:不支援。

Chrome 開發人員工具也支援 103 Early Hints,您可以在文件資源中看到 Link 標頭:

顯示「早期提示」標頭的網路面板
Chrome 開發人員工具中顯示的「Early Hints」Link標頭。

請注意,如要使用早期提示資源,請勿在開發人員工具中勾選 Disable cache,因為早期提示會使用瀏覽器快取。對於預先載入的資源,啟動程式會顯示為 early-hints大小則為 (Disk cache)

顯示早期提示啟動工具的網路面板
早期提示資源具有 early-hints 啟動器,並從磁碟快取載入。

這也需要 HTTPS 測試的受信任憑證。

Firefox (截至第 126 版) 在 DevTools 中沒有明確的 103 Early Hints 支援,但使用 Early Hints 載入的資源不會顯示 HTTP 標頭資訊,這也是透過 Early Hints 載入資源的一種指標。

伺服器支援

以下簡要說明熱門開放原始碼軟體 HTTP 伺服器軟體支援早期提示的程度:

以更輕鬆的方式啟用早期提示

如果您使用的是下列任一 CDN 或平台,可能就不需要手動導入提示。請參閱解決方案供應商的線上說明文件,瞭解該供應商是否支援 Early Hints,或參閱以下不完整清單:

如何避免用戶端不支援 Early 提示

雖然 100 範圍內的資訊 HTTP 回應屬於 HTTP 標準,但部分舊版用戶端或漫遊器可能難以應對這些挑戰,因為在 103 早期提示推出前,它們很少用於一般網路瀏覽。

請只針對傳送 sec-fetch-mode: navigate HTTP 要求標頭的用戶端,在回應中發出 103 早期提示,這樣才能確保這些提示只會傳送給瞭解如何等待後續回應的新版用戶端。此外,由於 Early Hints 僅支援導覽要求 (請參閱目前的限制),因此這項功能還有另一項優點,可避免在其他要求中不必要地傳送這些資訊。

此外,建議您只透過 HTTP/2 或 HTTP/3 連線傳送早期提示,且大多數瀏覽器只會透過這些通訊協定接受早期提示。

進階模式

如果您已在主要到達網頁中充分套用早期提示,但仍想尋找更多商機,不妨試試下列進階模式。

如果訪客在典型使用者旅程中提出第 n 次網頁要求,您可能要將 Early Hints 回應調整為網頁中較低層級和較深層的內容,也就是在較低優先順序的資源上使用 Early Hints。這聽起來可能違反直覺,因為我們建議您著重於高優先順序的轉譯阻斷子資源或來源。然而,在訪客瀏覽內容一段時間後,他們的瀏覽器很可能已具備所有重要資源。從那時起,您可能就該將重心轉移到優先順序較低的資源。舉例來說,這可能表示使用 Early Hints 載入產品圖片,或是只需要在較不常見的使用者互動中使用的額外 JS/CSS。

目前限制

以下是在 Chrome 中實作早期提示時的限制:

  • 僅適用於導覽要求 (也就是頂層文件的主要資源)。
  • 僅支援 preconnectpreload (也就是不支援 prefetch)。
  • 如果在最終回應中,早期提示後接著跨來源重新導向,Chrome 就會捨棄使用早期提示取得的資源和連線。
  • 使用 Early Hints 預先載入的資源會儲存在 HTTP 快取中,並在稍後由網頁擷取。因此,只有可快取的資源才能使用 Early Hints 預先載入,否則資源會重複擷取 (一次由 Early Hints 擷取,一次由文件擷取)。在 Chrome 中,如果 HTTPS 憑證不受信任,系統會停用 HTTP 快取 (即使您繼續載入頁面也是如此)。
  • 使用 HTTP <link> 標頭無法預先載入回應式圖片 (使用 imagesrcsetimagesizesmedia),因為系統必須先建立文件,才能定義螢幕可視區域。這表示 103 個早期提示無法用於預先載入回應式圖片,因此可能會載入不正確的圖片。請參閱這篇討論文章,瞭解如何更妥善地處理提案。

其他瀏覽器也有類似的限制,如先前所述,部分瀏覽器會進一步將 103 早期提示限制為僅限 preconnect

後續步驟

視社群的興趣而定,我們可能會在早期提示的實作中加入下列功能:

  • 針對無法快取的資源,使用記憶體快取而非 HTTP 快取的早期提示。
  • 針對子資源要求傳送早期提示。
  • 在 iframe 主要資源要求中傳送的早期提示。
  • 早期提示支援預先擷取。

歡迎提供意見,讓我們知道應優先處理哪些部分,以及如何進一步改善早期提示。

與 H2/Push 的關係

如果您熟悉已淘汰的 HTTP2/Push 功能,可能會想知道早期提示的差異。雖然 Early Hints 需要對瀏覽器來回往返,才能開始擷取重要的子資源,不過如果使用 HTTP2/Push,伺服器就能開始推送子資源,以及回應。雖然這聽起來令人驚豔,但卻引發了重大結構上的缺點:使用 HTTP2/Push 可避免推送瀏覽器既有的子資源,因此相當困難。這種「過度推送」效應會導致網路頻寬使用效率降低,大幅降低效能優勢。總體而言,Chrome 資料顯示,HTTP2/Push 對整個網站的效能而言,實際上是淨負面。

相反地,Early Hints 實際上的成效較佳,因為它結合了傳送初步回應的功能,還結合了提示,讓瀏覽器負責擷取或連線至實際需求。雖然早期提示無法涵蓋 HTTP2/Push 理論上可處理的所有用途,但我們認為早期提示是加快導覽速度的更實用解決方案。

縮圖圖片由 Pierre Bamin 提供。