HTTP 103 Early Hints:最終レスポンスより先に送るLinkヘッダーと、クライアントが越えてはいけない線

RFC 8297とRFC 9110をもとに、103 Early Hintsが何を送るのか、クライアントがヒントをどこまで使ってよいのか、103を複数回送る場合とHTTP/1.1で注意すべき点を整理しました。

開発Webネットワーク

HTMLレスポンスを作るのにデータベースの問い合わせで300msかかるとします。そのHTMLが読み込むCSSやJavaScriptはリクエストを受けた時点で決まっているのに、サーバーはその間Linkヘッダーひとつ送れません。最終レスポンスのヘッダーはステータスコードと一緒に出ていくもので、そのステータスコードが200になるか404になるかは問い合わせの結果を見るまでわからないからです。

RFC 8297で定義された103 Early Hintsは、この空白を埋めるステータスコードです。最終レスポンスに含まれる可能性が高いヘッダーフィールドを情報レスポンス(1xx)に入れて先に送り、サーバーはそのあとで時間のかかる処理を続けます。

やり取りの形

RFC 8297の2節の例をそのまま示します。

GET / HTTP/1.1
Host: example.com
HTTP/1.1 103 Early Hints
Link: </style.css>; rel=preload; as=style
Link: </script.js>; rel=preload; as=script

HTTP/1.1 200 OK
Date: Fri, 26 May 2017 10:02:11 GMT
Content-Length: 1234
Content-Type: text/html; charset=utf-8
Link: </style.css>; rel=preload; as=style
Link: </script.js>; rel=preload; as=script

<!doctype html>
[... 本文省略 ...]

103はヘッダーだけで本文を持ちません。クライアントは103のLinkフィールドにあるrel=preloadを見て、/style.cssの取得を先に始めることができます。クライアントがヒントを使う場合、時間の流れはおおよそ次のようになります。

クライアント                        サーバー
  |---- GET / ----------------------->|
  |<--- 103 Early Hints (Link) -------|  リクエストを受けてすぐ送る
  |                                   |  DB問い合わせ中
  |---- GET /style.css -------------->|  ヒントを見て先に取得
  |<--- 200 OK (HTML) ----------------|  問い合わせ完了後に最終レスポンス

Linkフィールドの構文そのものはRFC 8288の3節によります。<URI>のあとに;で区切ったパラメーターが続き、相対URIはRFC 3986の規則で解決します。preloadというリレーションとas属性は、RFC 8297が参照しているW3C Preload仕様のものなので、この記事では扱いません。

ヒントはあくまでヒント

RFC 8297の2節でいちばん重要なのは、103のヘッダーフィールドは最終レスポンスのヘッダーフィールドの代わりにはならない、という一文です。整理すると次のとおりです。

  • 最終レスポンスの処理に影響してはいけません(MUST NOT)。 103のヘッダーを見て何かを先に取得するといった性能上の最適化はしてかまいませんが、それ以外の点で最終レスポンスの扱いが103によって変わってはいけません。
  • 103自身のメタデータとして読んではいけません(MUST NOT)。 103に入っているContent-Typeなどのフィールドは103レスポンスを説明するものではなく、これから来る最終レスポンスの予告です。
  • 含まれていないフィールドから推測すべきではありません(SHOULD NOT)。 サーバーは最終レスポンスに入るフィールドの一部だけを103に入れてもかまいません(MAY)。103にないフィールドは最終レスポンスにもないだろう、と解釈してはいけません。
  • 最終レスポンスと違うことがあります。 通常は103で送ったフィールドを最終レスポンスにも入れますが、最終レスポンスを送る前に103の内容が正しくなかったとわかった場合などは、そうしないこともあると仕様は書いています。

サーバー側から見ると、クライアントに実際に適用されるべきヘッダーは、103に入れたとしても最終レスポンスに改めて入れる必要がある、ということです。103は最終レスポンスのヘッダーを先に知らせるだけで、先に適用させるものではありません。

103を複数回送る場合

ほかの1xxレスポンスと同じく、103も最終レスポンスの前に複数回届くことがあります。仕様は二つの場面を挙げています。

  1. 処理の途中で新しい情報がわかるたびに、サーバーが103を追加で送る場合(MAY)。すでに送ったフィールドを繰り返す必要はありませんが、繰り返してもかまいません。
  2. キャッシュを持つ仲介者が古くなった(stale)キャッシュ済みレスポンスのヘッダーから103を作って送り、続いて再検証リクエストに対してオリジンサーバーが返した103と最終レスポンスを転送する場合。

クライアントは複数の103で受け取ったフィールドを任意に組み合わせて、最終レスポンスに来るフィールドを予想してかまいません。仕様の二つ目の例は、予想が外れる場合を示しています。

HTTP/1.1 103 Early Hints
Link: </main.css>; rel=preload; as=style

HTTP/1.1 103 Early Hints
Link: </style.css>; rel=preload; as=style
Link: </script.js>; rel=preload; as=script

HTTP/1.1 200 OK
Date: Fri, 26 May 2017 10:02:11 GMT
Content-Length: 1234
Content-Type: text/html; charset=utf-8
Link: </main.css>; rel=preload; as=style
Link: </newstyle.css>; rel=preload; as=style
Link: </script.js>; rel=preload; as=script

二つの103で予告した三つのLinkのうち、/main.cssと/script.jsは最終レスポンスにもありますが、/style.cssは/newstyle.cssに置き換わっています。クライアントが/style.cssを先に取得していたなら、そのリクエストは結果的に無駄だったことになります。仕様はこれを禁じていないので、103には最終レスポンスにも入る見込みが十分に高いフィールドだけを選ぶほうが無駄は少なくなります。

1xxレスポンスに共通の規則

RFC 8297は1xxの一般的な規則を当時のRFC 7231から引いています。RFC 7231はその後RFC 9110に置き換えられ、該当する内容はRFC 9110の15.2節にあります。

  • 1xxレスポンスはヘッダーセクションの終わりで終わります。本文(content)もトレーラーも持てません。
  • クライアントは、最終レスポンスの前に届く一つ以上の1xxレスポンスを、予期していなくてもパースできなければなりません(MUST)。ユーザーエージェントは予期しない1xxレスポンスを無視してもかまいません(MAY)。
  • プロキシは、自分で1xxの生成を求めた場合を除き、1xxレスポンスを転送しなければなりません(MUST)。
  • HTTP/1.0には1xxのステータスコードが定義されていなかったため、サーバーはHTTP/1.0クライアントに1xxレスポンスを送ってはいけません(MUST NOT)。

同じ15章の冒頭は、一つのリクエストに対して0個以上の1xx中間レスポンスと、ちょうど一つの最終レスポンスが対応すると整理しています。103は性能上の最適化以外で最終レスポンスの処理に影響してはいけないので、103を理解できずに無視するユーザーエージェントも、最終レスポンスさえ正しく処理すれば済みます。問題になるのは、1xxを正しくパースすることすらできないクライアントです。

HTTP/1.1での危険

RFC 8297の3節(セキュリティに関する考察)はこの点を正面から扱っています。Expectヘッダーのないリクエストに情報レスポンスが返ることはまれだったため、103をうまく扱えないクライアントがありうる、ということです。

とくに、HTTP/1.1クライアントが103を最終レスポンスとして誤って処理すると、同じ接続で後に続くリクエストへのレスポンスをすべてその「最終レスポンス」の一部とみなす可能性が高くなります。レスポンスの境目がずれてしまうわけです。クライアントが異なるオリジンへのリクエストを一つの持続的接続にまとめて送っていると、これはクロスオリジンの情報漏えいの脆弱性になりえます。

そのため仕様は、クライアントが情報レスポンスを正しく扱えるとわかっている場合でなければ、サーバーはHTTP/1.1では103を送らないこともありうる、と書いています。HTTP/2のクライアントについては、レスポンスヘッダーの扱いがレスポンス本文の終わりの判定に影響しないので、このようなフレーミングの誤りは起きにくいと付け加えています。

サーバープッシュとの比較

RFC 8297の1節は、HTTP/2のサーバープッシュとも比べています。2017年当時の仕様が挙げたプッシュの限界は二つです。サーバーが権威を持つ(authoritative)リソースしか送れないこと、そしてクライアントがすでにキャッシュしていてもレスポンスが送られてしまうことです。103でLinkヘッダーを早めに送る方法は、最悪の場合サーバープッシュよりラウンドトリップが一回多くかかるものの、より柔軟で帯域の消費も少なくて済む可能性がある、と仕様は説明しています。先の二つの限界と照らし合わせると、リクエストを送るかどうかをクライアントが決めるので、すでにキャッシュにあるリソースは取り直さずに済む、という意味に読めます。

同じ節には、オリジンサーバーが103を使って、キャッシュを持つ仲介者でHTTP/2のサーバープッシュを起こすこともできると書かれています。

仕様の位置づけと、この記事の範囲

RFC 8297は標準化過程(Standards Track)の文書ではなく、実験的(Experimental)な文書です。103という番号と「Early Hints」という名前は、この文書によってIANAのHTTPステータスコードレジストリに登録されました(4節)。

この記事が根拠にしているのはRFC 8297、RFC 9110の15章、RFC 8288の3節だけです。どのブラウザーやCDN、Webサーバーが103に対応しているか、ブラウザーが103の中でpreload以外のどのフィールドやリレーションを実際に処理するかは、これらの仕様に書かれていないので扱っていません。使おうとしている実装の公式ドキュメントで確認してください。

参考にした文書

リンクと内容は2026年10月20日に確認しました。

ブログへ