Early Hints とともにサーバーの思考時間を使用してページ読み込みを高速化

重要なサブリソースに関するヒントをサーバーがブラウザに送信する仕組みについて説明します。

早期ヒントとは

ウェブサイトは時間の経過とともに高度化しています。そのため、リクエストされたページの HTML を生成するためにサーバーが重要な作業(データベースへのアクセス、配信元サーバーにアクセスする CDN など)が必要になることは珍しくありません。残念ながら、この「サーバーの思考時間」により、ブラウザがページのレンダリングを開始するまでに余分なレイテンシが発生します。実際、サーバーがレスポンスを準備する間、接続は実質的にアイドル状態になります。

ページの読み込みと他のリソースの読み込みの間に 200 ミリ秒のサーバー思考時間差があることを示す画像。
早期ヒントなし: メインリソースへの応答方法を決定するサーバー上ですべてがブロックされます。

早期ヒントは HTTP ステータス コード(103 Early Hints)で、最終レスポンスに先立って予備 HTTP レスポンスを送信する場合に使用します。これにより、サーバーがメインリソースの生成でビジー状態になっている間に、重要なサブリソース(ページのスタイルシート、重要な JavaScript など)やページで使用される可能性の高いオリジンについてのヒントをブラウザに送信できるようになります。ブラウザは、メインリソースを待機している間に、これらのヒントを使用して接続をウォームアップし、サブリソースをリクエストできます。言い換えれば、Early Hints はブラウザがそのような「サーバーの思考時間」を利用して事前になんらかの作業を行うことで、ページの読み込み時間を短縮するということです。

早期ヒントによってページから部分レスポンスを送信する仕組みを示す画像。
早期ヒントあり: サーバーは最終的なレスポンスを決定する間、リソースヒントを含む部分的なレスポンスを提供できます

Largest Contentful Paint のパフォーマンス向上は、ShopifyCloudflare で観察されている数百ミリ秒から最大 1 秒速くなることもあります(前後の比較を参照)。

2 つのサイトの比較。
WebPageTest によるテストウェブサイトでの Early Hints の使用前と Early Hints の比較(Moto G4 - DSL)

早期ヒントの使用方法

早期ヒントを活用するための最初のステップは、上位のランディング ページ、つまりユーザーがウェブサイトにアクセスしたときに通常最初に開始するページを特定することです。他のウェブサイトからアクセスしているユーザーが多い場合は、ホームページか、人気の商品リスティング ページを参考にします。これらのエントリ ポイントが他のページよりも重要であるのは、ユーザーがウェブサイト内を移動するにつれて初期ヒントの有用性が低下するためです(つまり、ブラウザは 2 回目または 3 回目のナビゲーションで必要なすべてのサブリソースを確保できる可能性が高くなります)。また、良い第一印象を与えることも重要です。

ランディング ページの優先順位リストができたら、次のステップは preconnect または preload のヒントの候補となる送信元またはサブリソースを特定することです。これらは通常、Largest Contentful PaintFirst Contentful Paint などの主要なユーザー指標に最も大きく影響するオリジンとサブリソースです。具体的には、同期 JavaScript、スタイルシート、さらにはウェブフォントなど、レンダリングをブロックするサブリソースを探します。同様に、ユーザーに関する主要な指標に大きく貢献するサブリソースをホストするオリジンを探します。

また、メインリソースですでに preconnect または preload を使用している場合は、これらのオリジンやリソースを早期ヒントの候補として検討できます。詳しくは、LCP を最適化する方法をご覧ください。ただし、preconnect ディレクティブと preload ディレクティブを HTML から Early Hints に単純にコピーすることは、最適ではない場合があります

これらを HTML で使用する場合は、通常、プリロード スキャナが HTML から検出できない preconnectpreload のリソース(たとえば、後で検出されるフォントや背景画像など)が必要になります。早期ヒントの場合、HTML は利用できないため、代わりに重要なドメインまたは preloadpreconnect を使用して、HTML の早い段階で検出できる重要なリソース(main.cssapp.js をプリロードするなど)を行います。また、一部のブラウザでは早期ヒントの preload がサポートされていません。ブラウザのサポートをご覧ください。

2 つ目のステップは、古くなったリソースやメインリソースで使用されなくなったリソースやオリジンで早期ヒントを使用するリスクを最小限に抑えることです。たとえば、頻繁に更新およびバージョニングされるリソース(example.com/css/main.fa231e9c.css など)は、最適な選択ではない場合があります。なお、この懸念事項は早期ヒントに固有のものではなく、preload または preconnect が存在する可能性がある場所であればどこでも適用されます。このような詳細は、自動化またはテンプレート化に最も適しています(たとえば、手動プロセスでは、リソースを使用する実際の HTML タグと preload の間でハッシュ URL またはバージョン URL の不一致が生じる可能性が高くなります)。

例として、次のフローについて考えてみましょう。

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)の 5 バージョン(abcd105)更新されています。

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

一般的に、かなり安定していて、メインリソースの結果から大きく依存しないリソースとオリジンを目指します。必要に応じて、主要なリソースを 2 つに分割することもできます。初期ヒントで使用するために設計された安定した部分と、メインのリソースがブラウザで受信された後に取得できる動的な部分です。

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

最後に、サーバーサイドで、早期ヒントをサポートすることが確認されているブラウザから送信されたメインリソース リクエストを探し、103 個の早期ヒントで即座に応答します。103 レスポンスには、関連する事前接続とプリロードのヒントを含めます。メインリソースの準備ができたら、通常のレスポンス(成功した場合は 200 OK など)でフォローアップします。下位互換性を確保するために、最終的なレスポンスに Link HTTP ヘッダーを含めることをおすすめします。たとえば、メインリソースの生成の一環として明らかになった重要なリソース(「2 つに分割」の提案に従った場合のキーリソースの動的部分など)で補うこともできます。次のようになります。

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 早期ヒントは主要なブラウザすべてでサポートされていますが、早期ヒントで送信できるディレクティブはブラウザによって異なります。

Preconnect サポート:

対応ブラウザ

  • 103
  • 103
  • 120
  • 17

プリロードのサポート:

対応ブラウザ

  • 103
  • 103
  • 123
  • x

Chrome DevTools では、103 早期ヒントをサポートしており、Link ヘッダーはドキュメント リソースで確認できます。

Early Hints ヘッダーが表示されている [Network] パネル
Chrome DevTools に早期ヒントの Link ヘッダーが表示されます。

Early Hints リソースを使用する際の注意事項。Early Hints はブラウザ キャッシュを使用するため、DevTools で Disable cache をオンにしないでください。プリロードされたリソースの場合、イニシエータearly-hintsサイズ(Disk cache) と表示されます。

Early Hints イニシエータが表示されている [Network] パネル
早期ヒントリソースには early-hints イニシエータがあり、ディスク キャッシュから読み込まれます。

HTTPS テスト用の信頼できる証明書も必要です。

Firefox(v126 時点)では DevTools での 103 早期ヒントの明示的なサポートはありませんが、早期ヒントを使用して読み込まれたリソースには、初期ヒントを通じて読み込まれたインジケーターの 1 つである HTTP ヘッダー情報が表示されません。

サーバー サポート

一般的なオープンソース ソフトウェアの HTTP サーバー ソフトウェアにおける Early Hints のサポートレベルの概要を次に示します。

早期ヒントを有効にする

次のいずれかの CDN またはプラットフォームを使用している場合は、早期ヒントを手動で実装する必要はありません。ソリューション プロバイダのオンライン ドキュメントで早期ヒントがサポートされているかどうかを確認するには、次のリストをご覧ください(すべてを網羅しているわけではありません)。

早期ヒントをサポートしていないお客様の問題を回避する方法

100 の範囲の情報 HTTP レスポンスは HTTP 標準の一部ですが、103 Early Hints のリリース前は一般的なウェブ ブラウジングではほとんど使用されなかったため、一部の古いクライアントや bot では苦労するかもしれません。

sec-fetch-mode: navigate HTTP リクエスト ヘッダーを送信するクライアントへのレスポンスで 103 個の早期ヒントを出力する場合にのみ、後続のレスポンスを待つことを理解している新しいクライアントにのみ、このようなヒントが送信されるようにする必要があります。また、早期ヒントはナビゲーション リクエストでのみサポートされているため(現在の制限事項を参照)、他のリクエストで不必要に送信しなくて済むというメリットもあります。

また、早期ヒントは HTTP/2 または HTTP/3 接続でのみ送信することをおすすめします。ほとんどのブラウザでは、これらのプロトコルでしか受信できません。

高度なパターン

主要なランディング ページに早期ヒントを全面的に適用していて、最適化案をさらに見つけようとしている場合は、次の高度なパターンが適しているかもしれません。

一般的なユーザー ジャーニーの一環として n 番目のページ リクエストを行っている訪問者に対しては、早期ヒントのレスポンスをページ内の下の方の深いコンテンツ、つまり優先度の低いリソースで早期ヒントを使用するのに合わせることができます。nth優先度が高く、レンダリングをブロックするサブリソースやオリジンに注力することをおすすめしたことを考えると、直感に反するかもしれません。ただし、ユーザーがしばらく操作した時点までに、ブラウザには重要なリソースがすでにすべて割り当てられている可能性が高くなります。そこから、優先度の低いリソースに注意を向けるのが合理的です。たとえば、Early Hints を使用して商品画像を読み込む場合や、あまり一般的でないユーザー操作にのみ必要となる追加の JS/CSS を使用する場合などです。

現在の制限事項

Chrome に実装されている早期ヒントの制限事項は次のとおりです。

  • ナビゲーション リクエスト(トップレベル ドキュメントのメインリソース)でのみ使用できます。
  • preconnectpreload のみをサポートします(つまり、prefetch はサポートされません)。
  • 早期ヒントの後、最終的なレスポンスでクロスオリジン リダイレクトが行われると、Chrome は早期ヒントを使用して取得したリソースと接続が切断されます。
  • 早期ヒントを使用してプリロードされたリソースは HTTP キャッシュに保存され、後でページによって HTTP キャッシュから取得されます。したがって、早期ヒントを使用してプリロードできるのは、キャッシュ可能なリソースのみです。あるいは、リソースは二重取得されます(早期ヒントで 1 回、ドキュメントで再び)。Chrome では、信頼できない HTTPS 証明書に対する HTTP キャッシュは無効になります(ページの読み込みを続行しても)。

他のブラウザにも同様の制限があり、前述のように、さらに一部のブラウザでは 103 早期ヒントを preconnect のみに制限しています。

次のステップ

コミュニティからの関心に応じて、次のような機能で早期ヒントの実装を補強する場合があります。

  • HTTP キャッシュではなくメモリ キャッシュを使用するキャッシュ不可リソースの早期ヒント。
  • サブリソース リクエストで送信される早期ヒント。
  • iframe メインリソース リクエストで送信される早期ヒント。
  • 早期ヒントでのプリフェッチのサポート。

優先すべき点や早期ヒントの改善方法について、皆様からのご意見をお待ちしております。

H2/Push との関係

非推奨になった HTTP2/push 機能をご存じであれば、Early Hints との違いをご存じでしょうか。Early Hints では、ブラウザが重要なサブリソースの取得を開始するためにラウンド トリップが必要になりますが、HTTP2/Push では、サーバーがレスポンスとともにサブリソースのプッシュを開始できます。これは驚くべきことのように聞こえますが、結果的に重要な構造上の欠点が生じました。HTTP2/Push では、ブラウザにすでに備わっているサブリソースのプッシュを回避することが非常に困難でした。この「過剰なプッシュ」効果により、ネットワーク帯域幅の使用効率が低下し、パフォーマンス上のメリットが大幅に損なわれていました。全体として、Chrome のデータによると、HTTP2/Push は実際にウェブ全体のパフォーマンスにおいて実質的にマイナスになっています。

対照的に、Early Hints は実際にはより優れたパフォーマンスを発揮します。事前応答を送信する機能と、実際に必要なものの取得や接続をブラウザが行うヒントを組み合わせたものだからです。Early Hints は HTTP2/Push で理論上対処できるすべてのユースケースを網羅しているわけではありませんが、早期ヒントの方がナビゲーションを高速化するためのより実用的なソリューションであると考えられます。

Pierre Bamin さんのサムネイル画像。