大きな画面での利用をおすすめします — アニメーションと操作がより快適になります。
入力

HTTP / HTTP/2 / HTTP/3

1 本の接続でページのファイルを取得するとき、HTTP のバージョンによって接続の共有方法と損失時の待ち方が変わります。本例の HTTP/1.1 はリクエストを順に送り、HTTP/2 は 1 本の TCP バイトストリームで多重化し、HTTP/3 は QUIC のストリーム単位でデータを届けます。

同じ 4 ファイルのページを 3 回見ます。HTTP/1.1 は階段状に取得します。HTTP/2 は並行転送しますが、CSS を含む TCP パケットを失うと、到着済みの JavaScript と画像も待たされます。HTTP/3 で同じパケットを失うと、JavaScript と画像は完了し、CSS が待ちます。リクエストの待ち行列、TCP の Head-of-Line ブロッキング(HOL)、ストリーム単位の配送という違いです。

接続を共有する仕組み

条件は 1 オリジン、1 接続です。ブラウザーは HTTP/1.1 で同一オリジンに約 6 本の接続を開くことがありますが、上限は実装に依存します。まず 1 本の動きを比較します。

この HTTP/1.1 モデルは keep-alive を使い、パイプライン化しません。in_flight は最大 1 です。GET /style.css の 200 レスポンスが完了するまで /app.js を送りません。損失時に遅れるのは未送信の待ち行列です。HTTP/1.1 の仕様には、複数リクエストを先に送るパイプラインもありますが、レスポンスは順序どおりに返す必要があります。

HTTP/2 は 1 本の TCP 接続と TLS セッションを使い、ALPN で h2 を選びます。クライアント開始のストリーム ID は奇数で、/ が 1、残りが 3、5、7 です。フレームを交互に送れるため、ウォーターフォールの棒が重なります。ただし TCP のシーケンスは共通です。TCP はバイトを順序どおりにアプリケーションへ渡すため、CSS のデータに欠落があると、後続の JavaScript と画像が受信バッファーにあっても HTTP/2 へ渡せません。

HTTP/3 は UDP 上の QUIC を使い、TLS 1.3 をトランスポートに統合します。本例の新規接続は 1 RTT で、TCP と TLS を分けた 2 RTT より短くなります。配送順序は STREAM ID とオフセットで管理しますが、損失検出と輻輳制御は接続に属します。パケット番号はストリームごとではなく、Initial、Handshake、Application Data の 3 空間に分かれます。0-RTT と 1-RTT は最後の空間を共有します。CSS のストリーム 4 だけを含むパケットを失っても、8 と 12 は配送できます。

フロントエンドの fetch オプションで HTTP/3 を選ぶわけではありません。オリジンや CDN が Alt-Svc または HTTPS DNS レコードで対応を知らせ、ブラウザーがハンドシェイクで h3 を交渉します。Chrome DevTools → Network の Protocol 列で h3、h2、http/1.1 を確認し、Timing やウォーターフォールと比べられます。損失のある経路ではストリーム単位の配送が効きますが、実際の高速化を保証するものではありません。

HTTP バージョンのステップ

初期設定の Compare は /、/style.css、/app.js、/photo.jpg の 4 ファイルを 1 接続で取得します。接続の確立を示した後、損失なしの HTTP/1.1、CSS パケットを失う HTTP/2、同じ損失の HTTP/3 を比較します。

  1. 初期化。 4 ファイルを表示し、待機中なのは / だけ。待ち行列、停止、分離という 3 つの挙動を示します。
  2. TCP。 3 ウェイハンドシェイクを 1 RTT にまとめ、接続行へ TCP ブロックを追加します。詳細は TCP Handshake の教材で扱います。
  3. TLS 1.3。 さらに 1 RTT。まだ HTTP は送っていません。接続の確立で 2 列を使います。
  4. GET /。200 が返り、rtt = 3、done = 1/4。
  5. HTML を解析。 残り 3 ファイルを発見し、待機列へ追加します。
  6. GET /style.css。接続を使用中。JavaScript と画像は待ちます。
  7. GET /app.js。CSS が完了し、画像はまだ待ちます。
  8. GET /photo.jpg。最後のファイルです。棒が重ならず、階段状になります。
  9. HTTP/2 へ。 追加ファイルを並行転送し、CSS データだけを含むトランスポートパケットを落とします。
  10. ストリーム 3、5、7 を開く。 3 個の HEADERS を 1 本の TCP 接続で送ります。
  11. DATA。 css、js、img が同じ列を共有します。これが多重化です。
  12. 損失。 CSS のパケットが欠落。JavaScript と画像は到着済みで、バッファーに残ります。
  13. TCP HOL。 追加ファイルがすべて止まり、後続バイトは欠落の補完を待ちます。
  14. HTTP/3 へ。 QUIC ストリームに切り替え、同じ損失を与えます。
  15. 再び DATA。 3 個のフレームを QUIC ストリーム 4、8、12 で送ります。
  16. 損失。 CSS は欠けますが、JavaScript と画像は配送できます。
  17. ストリームごとに進む。 JavaScript と画像は完了、CSS は未完了。CSS の失われたデータを新しいパケットで再送し、他のストリームは待ちません。
  18. 完了。 待ち行列、TCP の停止、ストリーム単位の配送を比べます。HTTP/3 を有効にするのはオリジンや CDN で、開発者ツールの Protocol 列には h3 と出ます。

単一バージョンのタブでも再生できます。Clean の完了は HTTP/1.1 が rtt = 6、HTTP/2 が 4、HTTP/3 が 3。Lost packet では HTTP/1.1 が 8 で、損失時の JavaScript は未送信です。HTTP/2 は 6 で、欠けた TCP データを再送すると全ストリームの配送が再開します。HTTP/3 は 5 で、再送待ちは CSS だけです。

パイプは現在の RTT のフレーム、ウォーターフォールは全 RTT の履歴です。H1 / H2 / H3 の区切りを使い、棒の重なりと CSS の赤い欠落が他のファイルを止めるかを見てください。

1 パケットの損失が 3 ファイルに及ぶ理由

HTTP/2 より前も、ブラウザーは複数の HTTP/1.1 接続で並行取得していました。HTTP/2 は TLS ハンドシェイクと輻輳ウィンドウを 1 つにまとめます。7 個目のスタイルシートも空き HTTP/1.1 ソケットを待つ必要はありません。ただし、ストリーム数とフロー制御の制限は残ります。

TCP の契約は単一の順序付きバイトストリームです。その上に HTTP の流れを多重化すると、同じ欠落に配送を止められます。QUIC は多重化を保ち、順序を保証する範囲を各ストリームに分けます。

損失なしの RTT は 6、4、3。内訳は次のとおりです。

  • HTTP/1.1:接続確立 2 + HTML 1 + 追加ファイル 3。
  • HTTP/2:接続確立 2 + HTML 1 + 並行取得 1。
  • HTTP/3:接続確立 1 + HTML 1 + 並行取得 1。

損失は欠落と再試行の 2 列を追加します。ただし HTTP/3 の JavaScript と画像は最初の追加ファイル RTT で終わります。これはモデルの集計であり、ベンチマークや実測 RTO ではありません。

境界条件

  • 追加ファイルは解析後に現れる。 本例では HTML が示す前に /style.css を送りません。実ブラウザーではプリロードスキャナーなどにより解析と取得を重ねられます。
  • HTTP/1.1 は未送信の待ち行列。 損失後にパイプが空でも、次の GET がまだ始まらないことがあります。
  • HTTP/2 は TCP の欠落待ち。 到着済みでも配送できないフレームがあります。「消失」ではなく「バッファー済み」です。
  • HTTP/3 の分離は配送順序の範囲。 接続を閉じれば全ストリームが終了します。1 パケットに CSS と JavaScript の STREAM フレームが入っていれば、両方が欠けたデータを待ちます。共有する輻輳制御、接続のフロー制御、QPACK の依存関係が他の流れを遅らせる場合もあります。
  • ID の規則が異なる。 HTTP/2 のクライアント開始は 1、3、5、7、偶数はサーバー用です。QUIC は下位 2 ビットで開始側と方向を表すため、HTTP/3 のクライアント双方向ストリームは 0、4、8、12 になります。

よくある間違い

  • HTTP/1.1 の 6 接続を単一接続の多重化と考える。 本例は各接続で 1 件ずつ処理します。HTTP/1.1 パイプラインは応答順序を守り、HTTP/2 は応答フレームを交互に送れます。ただし無制限ではありません。
  • HTTP/2 の多重化を HTTP/1.1 のパイプラインと同じにする。 後者は複数リクエストを先に送っても応答順序に縛られ、ブラウザーでは通常使いません。HTTP/2 の制約は TCP の損失によるものです。
  • HTTP/3 が常に速いと言う。 この新規接続モデルでは 1 RTT 減りますが、実際にはセッション再開や経路の状態にも依存します。
  • QUIC のパケット番号をストリームごとにする。 パケット番号は接続の 3 空間に属します。独立した配送に使うのは STREAM ID とオフセットです。
  • HTTPS の説明と混同する。 3 例とも暗号化済みです。HTTP/1.1 と HTTP/2 は TLS、HTTP/3 は QUIC に統合した TLS を使います。鍵の確立は TLS Handshake の教材で扱います。
  • ステータスコードだけを覚える。 200 は成功応答です。この教材の焦点は、誰が同時に送れ、損失時に誰が待つかです。

モデルの簡略化

実ブラウザーは HTTP/1.1 で同一オリジンに約 6 接続を開き、プリロード、解析、取得を重ねることがあります。HPACK と QPACK はヘッダーを圧縮します。HTTP/2 サーバープッシュは主要ブラウザーの実装から削除されましたが、仕様には残っています。優先度の対応は実装次第です。HTTP/3 の 0-RTT 早期データとセッション再開も省略しています。

失うのは CSS のデータだけを含む 1 トランスポートパケットです。TCP と TLS はまとめたステップで示し、RTT 数は 4 ファイルのモデル内の値です。仕様は RFC 9113(HTTP/2)、RFC 9114(HTTP/3)、RFC 9000 / 9002(QUIC)を参照してください。