重要なサブリソースに関するヒントをサーバーがブラウザに送信する方法について説明します。
早期ヒントとは
ウェブサイトは年々複雑になっています。そのため、リクエストされたページの HTML を生成するために、サーバーが簡単ではない処理(データベースへのアクセス、オリジン サーバーにアクセスする CDN など)を行う必要があることは珍しくありません。残念ながら、この「サーバー思考時間」により、ブラウザがページのレンダリングを開始するまでに余分な遅延が発生します。実際、サーバーがレスポンスを準備するまでの間、接続は事実上アイドル状態になります。
早期ヒントは、最終的なレスポンスの前に予備の HTTP レスポンスを送信するために使用される HTTP ステータス コード(103 Early Hints
)です。これにより、サーバーがメインリソースの生成に忙しい間に、クリティカルなサブリソース(ページのスタイルシート、クリティカルな JavaScript など)や、ページで使用される可能性が高いオリジンに関するヒントをブラウザに送信できます。ブラウザは、これらのヒントを使用して接続をウォームアップし、メイン リソースを待機しながらサブリソースをリクエストできます。つまり、Early Hints は、ブラウザが事前に一部の処理を行うことで、このような「サーバー思考時間」を活用し、ページの読み込みを高速化できるようにします。
Largest Contentful Paint のパフォーマンスが、Shopify や Cloudflare で観測された数百ミリ秒から、以下の前後比較に示すように最大 1 秒まで短縮される場合があります。
早期ヒントの使用方法
早期ヒントを活用する最初のステップは、上位のランディング ページ(ユーザーがウェブサイトにアクセスしたときに最初に表示されるページ)を特定することです。たとえば、ホームページや、他のウェブサイトからアクセスするユーザーが多い場合は人気商品のリスティング ページなどです。これらのエントリ ポイントが他のページよりも重要である理由は、ユーザーがウェブサイト内を移動するにつれて、早期ヒントの有用性が低下するためです(つまり、2 回目または 3 回目の移動では、ブラウザに必要なサブリソースがすべて存在している可能性が高いためです)。第一印象を良くすることも大切です。
優先順位付けされたランディング ページのリストが作成できたら、次は preconnect
ヒントまたは preload
ヒントの候補となるオリジンまたはサブリソースを特定します。通常、これらは、Largest Contentful Paint や First Contentful Paint などの主要なユーザー指標に最も貢献するオリジンとサブリソースです。具体的には、同期 JavaScript、スタイルシート、ウェブフォントなど、レンダリングをブロックするサブリソースを探します。同様に、主要なユーザー指標に大きく貢献しているサブリソースをホストするオリジンを探します。
また、メインのリソースですでに preconnect
または preload
を使用している場合は、これらのオリジンまたはリソースを早期ヒントの候補として検討できます。詳しくは、LCP を最適化する方法をご覧ください。ただし、preconnect
ディレクティブと preload
ディレクティブを HTML から早期ヒントに単純にコピーすることは最適ではない可能性があります。
HTML でこれらの属性を使用する場合は、通常、プリロード スキャナが HTML で検出しないリソース(フォントや背景画像など)を preconnect
または preload
します。早期ヒントでは HTML がないため、代わりに、HTML の早い段階で検出される可能性がある重要なドメインまたは重要なリソースに preconnect
を設定することをおすすめします。たとえば、main.css
や app.js
をプリロードします。また、すべてのブラウザが早期ヒントの preload
をサポートしているわけではありません。ブラウザのサポートをご覧ください。preload
2 つ目のステップでは、古くなった可能性のあるリソースやオリジン、またはメインリソースで使用されなくなったリソースやオリジンで早期ヒントを使用するリスクを最小限に抑えます。たとえば、頻繁に更新され、バージョニングされるリソース(example.com/css/main.fa231e9c.css
など)は最適な選択肢ではない可能性があります。この懸念は、早期ヒントに固有のものではなく、preload
や preconnect
が存在するすべての場所に適用されます。このような詳細は、自動化またはテンプレート処理で処理するのが最適です(たとえば、手動プロセスでは、preload
とリソースを使用する実際の HTML タグとの間でハッシュまたはバージョン 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">
最後に、サーバー側で、Early Hints をサポートしていることが判明しているブラウザから送信されたメイン リソース リクエストを探し、103 Early Hints ですぐに応答します。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 早期ヒントはすべての主要なブラウザでサポートされていますが、早期ヒントで送信できるディレクティブはブラウザによって異なります。
プリコネクトのサポート:
対応ブラウザ
プリロードのサポート:
対応ブラウザ
Chrome DevTools には 103 早期ヒントのサポートもあり、Link
ヘッダーはドキュメント リソースで確認できます。
なお、Early Hints リソースを使用するには、Early Hints がブラウザ キャッシュを使用するため、DevTools で Disable cache
をオンにしないでください。プリロードされたリソースの場合、イニシエーターは early-hints
、サイズは (Disk cache)
と表示されます。
また、HTTPS テスト用の信頼できる証明書も必要です。
Firefox(v126 時点)では、DevTools で 103 早期ヒントを明示的にサポートしていませんが、早期ヒントを使用して読み込まれたリソースには HTTP ヘッダー情報が表示されません。これは、早期ヒントによって読み込まれたことを示す 1 つの指標です。
サーバー サポート
一般的なオープンソース ソフトウェアの HTTP サーバー ソフトウェアにおける早期ヒントのサポートレベルの概要は次のとおりです。
- Apache: mod_http2 を使用してサポートされています。
- H2O: サポート対象。
- NGINX: 試験運用版モジュール。
- Node: 18.11.0 以降、http と http2 でサポート
簡単な方法で早期ヒントを有効にする
次のいずれかの CDN またはプラットフォームを使用している場合は、早期ヒントを手動で実装する必要がない場合があります。ソリューション プロバイダのオンライン ドキュメントで、Early Hints がサポートされているかどうかを確認するか、こちらに記載されている一覧(ただし、網羅的なものではありません)をご覧ください。
早期ヒントをサポートしていないクライアントで問題を回避する方法
100 番台の情報提供用の HTTP レスポンスは HTTP 標準の一部ですが、103 早期ヒントのリリース前は一般的なウェブ ブラウジングで使用されることがほとんどなかったため、古いクライアントやボットでは対応できない場合があります。
sec-fetch-mode: navigate
HTTP リクエスト ヘッダーを送信するクライアントにのみ 103 早期ヒントを出力することで、後続のレスポンスを待機することを理解している新しいクライアントにのみ、そのようなヒントが送信されるようにする必要があります。また、早期ヒントはナビゲーション リクエストでのみサポートされているため(現在の制限事項を参照)、他のリクエストで不要に送信されるという問題も回避できます。
また、早期ヒントは HTTP/2 接続または HTTP/3 接続でのみ送信することをおすすめします。ほとんどのブラウザでは、これらのプロトコルでのみ早期ヒントを受け入れます。
高度なパターン
主要なランディング ページに早期ヒントをすべて適用し、さらに機会を探している場合は、次の高度なパターンを検討してください。
典型的なユーザー ジャーニーの一環としてn 回目のページ リクエストを行っている訪問者に対しては、ページ内の下位のコンテンツに Early Hints レスポンスを適応させ、優先度の低いリソースで Early Hints を使用することをおすすめします。優先度が高く、レンダリングをブロックするサブリソースやオリジンに重点を置くことを推奨しているため、これは直感に反するように思えます。ただし、ユーザーがサイトを閲覧しているうちに、重要なリソースはすべてブラウザに読み込まれている可能性が高いです。その後は、優先度の低いリソースに注意を向けるのがよいでしょう。たとえば、Early Hints を使用して商品画像を読み込む場合や、あまり一般的でないユーザー操作でのみ必要な追加の JS/CSS などです。
現在の制限事項
Chrome で実装されている早期ヒントの制限事項は次のとおりです。
- ナビゲーション リクエスト(最上位ドキュメントのメイン リソース)でのみ使用できます。
preconnect
とpreload
のみをサポートします(prefetch
はサポートされていません)。- 初期ヒントの後に最終レスポンスでクロスオリジン リダイレクトが発生すると、Chrome は初期ヒントを使用して取得したリソースと接続を破棄します。
- 早期ヒントを使用してプリロードされたリソースは HTTP キャッシュに保存され、後でページから取得されます。したがって、キャッシュに保存できるリソースのみを早期ヒントを使用してプリロードできます。そうしないと、リソースが(早期ヒントとドキュメントによって)2 回取得されます。Chrome では、信頼できない HTTPS 証明書に対して HTTP キャッシュが無効になります(ページの読み込みを続行した場合でも同様です)。
- ドキュメントが作成されるまでビューポートが定義されないため、レスポンシブ画像(
imagesrcset
、imagesizes
、media
を使用)のプリロードは、HTTP<link>
ヘッダーではサポートされていません。つまり、103 早期ヒントはレスポンシブ画像のプリロードには使用できず、この目的で使用すると間違った画像が読み込まれる可能性があります。この問題をより適切に処理する方法に関する提案に関するディスカッションをご覧ください。
他のブラウザにも同様の制限があり、前述のとおり、一部のブラウザでは 103 早期ヒントを preconnect
のみに制限しています。
次のステップ
コミュニティからの関心によっては、Early Hints の実装に次の機能を追加する可能性があります。
- HTTP キャッシュではなくメモリキャッシュを使用するキャッシュに保存できないリソースの早期ヒント。
- サブリソース リクエストで送信される早期ヒント。
- iframe のメイン リソース リクエストで送信される早期ヒント。
- 早期ヒントのプリフェッチをサポート。
優先すべき点や、早期ヒントをさらに改善する方法について、ご意見をお寄せください。
H2/プッシュとの関係
非推奨の HTTP2/Push 機能に精通している場合は、Early Hints とどのように異なるのか疑問に思うかもしれません。Early Hints では、ブラウザが重要なサブリソースの取得を開始するためにラウンドトリップが必要になりますが、HTTP2/Push では、サーバーがレスポンスとともにサブリソースのプッシュを開始できます。これは素晴らしいように聞こえますが、構造的な大きな欠点がありました。HTTP2/Push では、ブラウザにすでに存在するサブリソースをプッシュしないようにすることは非常に困難でした。この「過剰プッシュ」効果により、ネットワーク帯域幅の使用効率が低下し、パフォーマンスの向上が大幅に妨げられました。全体的に、Chrome のデータは、HTTP2/Push がウェブ全体のパフォーマンスに悪影響を及ぼしていることを示しています。
一方、Early Hints は、事前レスポンスの送信機能と、実際に必要なものの取得や接続をブラウザに任せるヒントを組み合わせているため、実際にはパフォーマンスが向上します。理論上、HTTP2/Push で対応できるすべてのユースケースを Early Hints でカバーできるわけではありませんが、ナビゲーションを高速化するためのより実用的なソリューションであると考えています。
サムネイル画像: Pierre Bamin