
HTTP/3 と QUIC 入門 - なぜ HTTP は TCP を捨てて UDP に移ったのか
Web ページを開くとき、ブラウザとサーバーの間では長らく HTTP over TCP が使われてきました。HTTP/1.1 も HTTP/2 も、下では TCP が動いています。ところが最新の HTTP/3 は、TCP をやめて UDP の上で動く QUIC(クイック)という新しいトランスポートに乗り換えました。「せっかく信頼性のある TCP があるのに、なぜわざわざ UDP に戻るのか」と不思議に思う人も多いはずです。
この記事では、その理由を一次ソースの RFC を軸に整理します。まず HTTP/2 が TCP の上で抱える Head-of-Line(HoL)ブロッキングという問題から出発し、QUIC がそれをどう解決するのか、ストリーム多重化・TLS 1.3 統合・0-RTT・コネクションマイグレーションといった仕組みを直感的に見ていきます。ネットワークの前提知識として DNS の名前解決や 公開鍵暗号・デジタル署名を知っておくと理解が深まります。
HTTP/2 までの復習
まず、これまでの HTTP がどう進化してきたかを整理します。
- HTTP/1.1: 1本の TCP コネクション上でリクエストとレスポンスを1往復ずつ処理します。前のレスポンスが返るまで次を送れない(正確にはパイプライン化はあるが実用されない)ため、ブラウザはドメインごとに複数の TCP コネクションを張って並列化していました。
- HTTP/2: RFC 7540(後継は RFC 9113)で標準化されました。1本の TCP コネクションの中をストリームという論理的な通り道に分割し、複数のリクエスト/レスポンスを多重化(マルチプレクシング)できるようにしました。ヘッダーは HPACK(RFC 7541)で圧縮されます。
HTTP/2 の多重化は大きな前進でした。1本の接続で数十のリソースを同時に取得でき、HTTP/1.1 時代のように接続をたくさん張る必要がなくなりました。
しかし、この「1本の TCP コネクションに全部まとめる」という設計こそが、次に説明する新たなボトルネックを生みます。ステータスコードの意味を復習したい場合は HTTP ステータスコード入門もあわせてどうぞ。
TCP のせいで詰まる - HoL ブロッキング
HTTP/2 のストリームは、アプリケーションから見れば独立しています。しかしその下の TCP から見ると、すべてのストリームのデータは1本の連続したバイト列にまとめられて流れています。
TCP は「送ったデータを、送った順番どおりに、欠けなく相手に届ける」ことを保証します。この「順番どおり」がくせ者です。途中のパケットが1つでも失われると、TCP はそのパケットが再送されて到着するまで、後続のすべてのデータをアプリケーションに渡しません。たとえ後続パケットが先に届いていても、順序を守るために待たされます。
これが Head-of-Line(HoL)ブロッキングです。HTTP/2 では、本来無関係な複数のストリームが1本の TCP に相乗りしているため、あるストリームのための1パケットが落ちただけで、無関係な他のストリームまで全部止まるのです。
TCP の1本のバイト列(順序保証)
┌──────────────────────────────────────────┐
│ ...A2 │ [B1: 欠落] │ A3 │ B2 │ C1 │ C2 ...│
└──────────────────────────────────────────┘
↑
ここが届かないと、
後ろの A3 / B2 / C1 / C2 が
すでに届いていてもアプリに渡せない
(= 全ストリームが待たされる)重要なのは、これは HTTP/2 の実装が悪いのではなく、TCP という土台の性質だという点です。TCP はストリームの区別を知らないので、HTTP/2 がいくら論理的にストリームを分けても、TCP レイヤーの HoL ブロッキングは避けられません。HTTP/2 は「アプリケーション層の HoL ブロッキング」は解消しましたが、「トランスポート層の HoL ブロッキング」は TCP を使う限り残ってしまうのです。
パケットロスが起きやすいモバイル回線や遅延の大きい経路ほど、この影響は顕著になります。
QUIC とは - UDP の上に作り直したトランスポート
この根本問題を解くには、TCP そのものを置き換えるしかありません。そこで登場したのが QUIC です。
QUIC は RFC 9000「QUIC: A UDP-Based Multiplexed and Secure Transport」(2021年5月発行)で標準化された、UDP の上で動く新しいトランスポートプロトコルです。UDP はパケットを送るだけの最小限のプロトコルで、順序保証も再送もありません。QUIC はその上に、TCP が持っていた信頼性・順序制御・輻輳制御を、しかもストリームごとに独立させて再実装しています。
NOTE
なぜ TCP を改造せず UDP を選んだのか。TCP は OS カーネルに深く組み込まれ、ルーターやファイアウォールなどの中間装置(ミドルボックス)も TCP の中身を前提に動いています。TCP に新機能を足しても、世界中の機器が対応するまで何年もかかります。UDP の上にユーザー空間で実装すれば、アプリやブラウザの更新だけで新機能を配れます。QUIC はこの「デプロイのしやすさ」を重視して UDP を土台に選びました。
QUIC が提供する主な機能は、RFC 9000 の言葉を借りると「フロー制御されたストリーム」「低遅延なコネクション確立」「ネットワークパスのマイグレーション」です。さらに暗号化と認証がプロトコルに組み込まれています。QUIC はもともと Google が開発し、IETF で標準化される過程で TLS 1.3 ベースの設計に整理されました。
QUIC の主要な仕組み
QUIC の要点を4つに分けて見ていきます。
ストリーム多重化 - HoL ブロッキングの解消
QUIC では、各ストリームが独立した順序保証を持ちます。ストリーム A のパケットが失われても、QUIC はストリーム B・C のデータはそのままアプリケーションに渡せます。失われたのはあくまでストリーム A の一部だけなので、他のストリームを巻き込みません。
UDP 上の QUIC(ストリームは互いに独立)
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Stream A │ │ Stream B │ │ Stream C │
│ A1 [欠落] A3 │ │ B1 B2 B3 ✔ │ │ C1 C2 ✔ │
└───────────────┘ └───────────────┘ └───────────────┘
↑ ここだけ待つ B と C はそのまま
アプリに渡せるこれが QUIC 最大のポイントです。TCP の HoL ブロッキングは「トランスポートが1本のバイト列」だから起きていました。QUIC はトランスポート自身がストリームを理解し、ストリーム単位で再送と順序制御を行うため、あるストリームのロスが他に波及しません。
TLS 1.3 の統合 - 暗号化が標準装備
QUIC は暗号化がオプションではなく必須です。その暗号は RFC 9001「Using TLS to Secure QUIC」(2021年5月発行)で定義され、TLS 1.3 を使います。
従来の HTTPS は「TCP でハンドシェイク(1往復)」してから「その上で TLS ハンドシェイク(TLS 1.3 なら1往復)」と、接続確立に段階を踏んでいました。QUIC は TLS 1.3 をトランスポートに融合させ、トランスポートのハンドシェイクと暗号のハンドシェイクを一体で行います。RFC 9001 の表現では、TLS のハンドシェイクメッセージは QUIC の CRYPTO フレームに載せて直接運ばれ、QUIC が TLS のレコード層の役割を引き受けます。
結果として、初回接続でも 1-RTT(1往復)で暗号化された通信を始められます。TLS の基礎を押さえたい方は 公開鍵暗号・デジタル署名入門を参照してください。
0-RTT - 再訪問なら往復ゼロでデータ送信
一度接続したことのあるサーバーへ再び接続する場合、QUIC は前回のセッション情報を再利用して 0-RTT(往復ゼロ)で最初のアプリケーションデータを送れます。ハンドシェイクの完了を待たずに、最初のパケットにリクエストを載せてしまうイメージです。
これは体感速度に大きく効きますが、注意点もあります。0-RTT で送るデータはリプレイ攻撃(攻撃者が同じパケットを再送する攻撃)に対して脆弱になり得るため、副作用のないべき等なリクエスト(GET など)に限定するのが原則です。べき等性については REST API 設計とべき等性もあわせてどうぞ。
HTTP/2 (TCP+TLS1.3) : TCP 1-RTT + TLS 1-RTT = 実質2往復
HTTP/3 初回 (QUIC) : 1-RTT でハンドシェイク兼データ開始
HTTP/3 再訪 (QUIC) : 0-RTT で最初のデータを即送信コネクションマイグレーション - IP が変わっても切れない
TCP のコネクションは「送信元 IP・ポート・宛先 IP・ポート」の4つの組(4-tuple)で識別されます。そのため、Wi-Fi からモバイル回線に切り替わって IP アドレスが変わると、TCP コネクションは切れてしまい、TLS ハンドシェイクからやり直しになります。
QUIC は接続を IP アドレスではなく Connection ID という識別子で管理します。IP アドレスが変わっても Connection ID が同じなら、サーバーは同じ接続だと認識でき、通信を継続できます。これがコネクションマイグレーションです。スマホを持ってカフェの Wi-Fi から外に出た瞬間でも、ダウンロードや通話が途切れにくくなります。
損失検知と輻輳制御
QUIC の再送・輻輳制御は RFC 9002「QUIC Loss Detection and Congestion Control」(2021年5月発行)で定義されています。TCP の Fast Retransmit に相当する ACK ベースの損失検知や、テール損失に備える Probe Timeout、NewReno 相当の輻輳制御などが規定されています。QUIC はパケット番号が常に単調増加する(再送でも番号を使い回さない)ため、TCP のような再送のあいまいさがなく、損失検知が簡潔になっています。
HTTP/3 = QUIC の上の HTTP
ここまでの QUIC はあくまでトランスポートです。その上に HTTP のセマンティクス(メソッド・ヘッダー・ステータスコードなど)を載せたものが HTTP/3 で、RFC 9114「HTTP/3」(2022年6月発行)で標準化されています。
HTTP/2 HTTP/3
┌───────────────┐ ┌───────────────┐
│ HTTP semantics│ │ HTTP semantics│
├───────────────┤ ├───────────────┤
│ HTTP/2 frames │ │ HTTP/3 frames │
│ HPACK │ │ QPACK │
├───────────────┤ ├───────────────┤
│ TLS 1.2/1.3 │ │ QUIC │
├───────────────┤ │ (TLS 1.3統合)│
│ TCP │ ├───────────────┤
│ │ │ UDP │
└───────────────┘ └───────────────┘HTTP/3 では、ストリーム多重化・フロー制御・低遅延なコネクション確立といった機能をすべて QUIC に任せます。HTTP/3 自体は HTTP の意味づけとフレーミングに専念できるので、HTTP/2 よりも仕様がすっきりしています。
一点だけ、ヘッダー圧縮は作り直しが必要でした。HTTP/2 の HPACK は「全ストリームにまたがる完全な順序」を前提にしています。しかし QUIC はストリーム間で順不同の到着を許すため、HPACK をそのまま使うと再びヘッダー用の HoL ブロッキングが発生してしまいます。そこで HTTP/3 は RFC 9204「QPACK: Field Compression for HTTP/3」(2022年6月発行)で定義された QPACK を使い、順不同到着でも正しく動くように設計されています。
まとめると、HTTP/3 を支える主な RFC は次のとおりです。
| RFC | タイトル | 内容 |
|---|---|---|
| RFC 9000 | QUIC: A UDP-Based Multiplexed and Secure Transport | QUIC トランスポート本体 |
| RFC 9001 | Using TLS to Secure QUIC | QUIC と TLS 1.3 の統合 |
| RFC 9002 | QUIC Loss Detection and Congestion Control | 損失検知・輻輳制御 |
| RFC 9114 | HTTP/3 | QUIC 上の HTTP マッピング |
| RFC 9204 | QPACK: Field Compression for HTTP/3 | HTTP/3 用ヘッダー圧縮 |
いま使えるのか - ブラウザ・サーバー・CDN の対応
HTTP/3 はすでに広く使える段階に入っています。
ブラウザ: Chromium 系(Chrome / Edge / Opera など)は2020年4月からデフォルトで HTTP/3 に対応しています。Firefox も 88 以降でデフォルト有効です。Safari は長らく実験的機能でしたが、Safari 16 以降で標準対応が進みました(各ブラウザの詳細バージョンは公式リリースノートで確認してください)。
CDN: Cloudflare をはじめとする主要 CDN は HTTP/3 に対応済みで、対応ドメインでは自動的に HTTP/3 が使われることが多いです。CDN のキャッシュ設計は CDN と Cache-Control / s-maxage 実践も参考にしてください。
サーバー: nginx は 1.25.0 以降で QUIC / HTTP/3 に対応しました(listen に quic パラメータを追加)。ただし公式ドキュメント上は実験的(experimental)という位置づけで、ビルドには QUIC 対応の TLS ライブラリが必要です。本番導入時は、利用する nginx(またはフォークや他サーバー)のバージョンとステータスを必ず一次情報で確認してください。
普及状況: Cloudflare Radar などの計測では、HTTP/3 のトラフィックシェアはおおむね2割前後で推移しているとされます(時点・観測点により変動し、HTTP/2 が依然として最大勢力という報告もあります)。具体的な数値は観測元と時期に強く依存するため、参考値・概数として扱ってください(本記事では特定時点の断定は避けます)。
NOTE
HTTP/3 は「Alt-Svc」という仕組みで段階的に使われ始めます。多くのブラウザは最初 HTTP/2 で接続し、レスポンスの Alt-Svc ヘッダーでサーバーが HTTP/3 対応を告知すると、次回以降 QUIC 接続を試みます。つまり、初回アクセスがいきなり HTTP/3 になるとは限りません。
自分で確認してみる
自分のブラウザやサイトが HTTP/3 を使っているかは、いくつかの方法で確認できます。
ブラウザの DevTools: ネットワークタブを開き、列の設定で「Protocol」を表示させます。HTTP/3 で取得されたリクエストは h3 と表示されます(HTTP/2 なら h2、HTTP/1.1 なら http/1.1)。
Alt-Svc ヘッダー: レスポンスヘッダーに Alt-Svc: h3=":443" のような告知があれば、そのサーバーは HTTP/3 に対応しています。
curl: HTTP/3 対応ビルドの curl であれば --http3 オプションで試せます。ただし標準配布の curl には HTTP/3 が含まれないことが多く、QUIC 対応ライブラリと組み合わせた特別ビルドが必要です。
# HTTP/3 を試し、ダメなら下位バージョンにフォールバック
curl --http3 -I https://example.com/
# HTTP/3 のみ(失敗したら通信させない・確認用に厳密)
curl --http3-only -I https://example.com/
# 応答ヘッダーで Alt-Svc(h3 告知)を確認
curl -sI https://example.com/ | grep -i alt-svcHTTP/3 の注意点・トレードオフ
HTTP/3 は万能ではありません。導入・運用時に押さえておきたい点があります。
- UDP がブロックされる環境がある: 一部の企業ネットワークやファイアウォールは UDP/443 を通しません。その場合ブラウザは HTTP/2(TCP)にフォールバックします。HTTP/3 は「速くする追加の選択肢」であり、TCP 経路がなくなるわけではありません。
- CPU コストが高くなりがち: QUIC の暗号処理やパケット処理はユーザー空間で行われることが多く、TCP + カーネルの TLS オフロードに比べて CPU を食う傾向があります。高帯域・低損失な回線では、必ずしも HTTP/3 が HTTP/2 より速いとは限りません。
- 0-RTT のリプレイリスク: 前述のとおり、0-RTT データは再送攻撃に注意が必要で、副作用のあるリクエストには使うべきではありません。
- 運用ツールの成熟度: パケットキャプチャや解析、負荷分散などのツール群は、TCP に比べると QUIC 対応がまだ発展途上な部分があります。
- デバッグの難しさ: 通信全体が暗号化されているため、中身を覗いての切り分けは TCP + 平文時代より難しくなります。
つまり HTTP/3 は「高遅延・高損失なモバイル/長距離回線で特に効く」プロトコルであり、環境によっては HTTP/2 のままで十分なケースもあります。導入は効果測定とセットで考えるのが現実的です。
まとめ
- HTTP/2 はストリーム多重化を実現したが、土台の TCP が「1本の順序付きバイト列」であるため、トランスポート層の HoL ブロッキングを解消できなかった。
- QUIC(RFC 9000)は UDP の上に、ストリームごとに独立した信頼性・順序制御・輻輳制御(RFC 9002)を実装し、この HoL ブロッキングを根本から解決する。
- QUIC は TLS 1.3 を統合(RFC 9001)し、暗号化を必須にしつつ、初回 1-RTT・再訪 0-RTT の低遅延な接続確立を実現する。
- Connection ID により IP が変わっても接続が切れないコネクションマイグレーションが可能。
- HTTP/3(RFC 9114)は QUIC の上に HTTP を載せたもので、順不同到着に対応した QPACK(RFC 9204)でヘッダーを圧縮する。
- ブラウザ・CDN の対応は進み、nginx なども対応済み(実験的)だが、UDP ブロックや CPU コスト、0-RTT リスクなどのトレードオフもあるため、効果測定とセットで導入するのがよい。
HTTP は、意味づけ(セマンティクス)を保ったまま、その土台を TCP から QUIC へと入れ替えました。「なぜ TCP を捨てて UDP に移ったのか」という問いの答えは、TCP の順序保証そのものが多重化のボトルネックだったからに尽きます。QUIC はその制約を、トランスポートを作り直すことで乗り越えたのです。


