RFC 9113: HTTP/2
HTTP/2のバイナリフレーミングとストリーム多重化をRFC 9113で精読し、優先度ツリーの非推奨化やサーバープッシュ衰退の背景、HTTP/3へつながるTCPの限界まで一気に把握できる。
- RFC 9113(2022年)はRFC 7540(2015年)を置換し、HTTPの意味論を9110へ委譲した。自身はバイナリフレームと多重化に専念し、ヘッダ圧縮はHPACK(7541)を継続する。
- 1本のTCP接続へ識別子付きHEADERS/DATAフレームを混在させて多重化する。ストリームIDはクライアント起点が奇数、サーバー起点が偶数で、HTTP/1.1の複数接続を置換する。
- 複雑な優先度ツリーは9113で非推奨となりRFC 9218へ移行、サーバープッシュもChromeが2022年に廃止した。TCPの頭詰まりは残り、HTTP/3(RFC 9114)誕生の要因になった。
このRFCが決めたこと
横にスクロール
RFC 9113(2022年6月)は、HTTP/2を定義する現行の文書だ。初版のRFC 7540(2015年5月)を置き換え、TLS 1.3上での利用条件を定めていたRFC 8740も統合した。メソッドやステータスコードの意味論は同時期発行のRFC 9110へ委譲されたため、9113が定めるのは「その意味論を1本のTCP接続でどう運ぶか」というフレーミングレイヤーに絞られている(意味論側はRFC 9110: HTTPセマンティクスで精読した)。
HTTP/1.1がテキスト行を読み書きするプロトコルだったのに対し、HTTP/2はすべての通信をバイナリのフレームに分解し、複数のリクエストを同じ接続に混在させる。なお、ヘッダ圧縮を担うHPACKは別文書のRFC 7541のままで、9113でも変更されていない。
GETの意味も404の意味もHTTP/1.1と完全に同じだ。HTTP/2は転送の仕組みだけを作り直したバージョンであり、だからこそ意味論をRFC 9110へ切り出す再編が成立した。
要点の精読
バイナリフレーミング
HTTP/2の通信は、9バイトの固定ヘッダを持つフレームの連続だ。ヘッダには長さ(24ビット)、タイプ(8ビット)、フラグ(8ビット)、ストリームID(31ビット)が並び、その後にペイロードが続く。行指向のテキストだったHTTP/1.1と違い、境界が長さフィールドで機械的に決まるため、解析が単純で曖昧さも入り込みにくい。
+-----------------------------------------------+
| Length (24) |
+---------------+---------------+---------------+
| Type (8) | Flags (8) |
+-+-------------+---------------+---------------+
|R| Stream Identifier (31) |
+=+=============================================+
| Frame Payload ... |
+-----------------------------------------------+
| フレームタイプ | 役割 |
|---|---|
| HEADERS | ヘッダ群をHPACK圧縮して運び、ストリームを開始する |
| DATA | リクエストやレスポンスの本体を運ぶ |
| SETTINGS | 最大同時ストリーム数など接続単位の設定を交換する |
| RST_STREAM | 接続を切らずに特定のストリームだけを中断する |
| GOAWAY | 接続全体の終了を予告し、処理済みの最終ストリームIDを伝える |
接続の最初にクライアントは PRI * HTTP/2.0 で始まる固定の接続プリフェイスを送り、双方がSETTINGSフレームを交換してから本来の通信が始まる。
ストリーム多重化とストリームID
HTTP/2は1本のTCP接続の中に「ストリーム」という論理的な通信路を複数作る。各フレームはストリームIDを持つので、受信側は混在して届くフレームを元のリクエスト単位に組み立て直せる。HTTP/1.1では1つの接続で同時に処理できるリクエストが実質1つで、ブラウザは並列化のために接続を6本程度張っていたが、HTTP/2では1本で足りる。
ストリームIDの割り当てには規則がある。クライアントが開始するストリームは奇数、サーバーが開始するストリーム(サーバープッシュ用)は偶数、そしてID 0は接続そのものを指し、SETTINGSやPINGなど接続全体の制御に使う。IDは単調増加で再利用されず、使い切ったら新しい接続を張り直す。
さらにWINDOW_UPDATEフレームによるフロー制御が、ストリーム単位と接続単位の2段階で働く。巨大なレスポンス1本が帯域を占有し、他のストリームを飢えさせるのを防ぐ仕組みだ。
HPACKヘッダ圧縮はRFC 7541のまま
HTTP/2の前身にあたるSPDYはヘッダをzlibで圧縮していたが、2012年のCRIME攻撃により、TLSで暗号化していても圧縮後の長さからCookieなどの秘密を推測できることが示された。この教訓から専用設計されたのがHPACK(RFC 7541、2015年5月)で、頻出ヘッダを引く61エントリの静的テーブル、接続ごとに育つ動的テーブル、ハフマン符号の3つを組み合わせる。
重要なのは、HPACKが送信側と受信側で動的テーブルの状態を同期し続けるステートフルな方式だという点だ。9113はこのHPACKに一切手を入れず、そのまま参照している。なお、パケットの到着順序が保証されないQUIC上ではこの同期が成立しないため、HTTP/3では後継のQPACK(RFC 9204)が別途設計されることになった。
優先度ツリーの非推奨化とサーバープッシュの衰退
RFC 7540は、ストリーム間の依存関係を木構造で表し、1〜256の重みを付ける精緻な優先度制御を定義していた。しかし接続ごとに木を管理するコストが高いうえ解釈も実装ごとにばらつき、主要サーバーの多くが優先度指定を無視している実態が明らかになる。結果としてRFC 9113はこの優先度シグナリングを非推奨(deprecated)とし、後継にはurgency(0〜7)とincrementalフラグだけの単純な方式がRFC 9218(2022年6月)として切り出された。
サーバープッシュも似た運命を辿った。PUSH_PROMISEフレームでサーバーがリクエストを先回りして送り込む仕組みだが、サーバーはブラウザのキャッシュ状態を知らないため、既に持っているリソースを押し付けて帯域を浪費しがちだった。効果測定でも利得を示せず、Chromeは2022年のバージョン106でHTTP/2プッシュ対応を打ち切った。現在は103 Early Hints(RFC 8297)とpreloadによる先読みが実務の代替になっている。9113にプッシュの仕様自体は残るが、SETTINGSで無効化できる扱いだ。
h2はTLSが事実上必須、それでもTCPの限界は残る
HTTP/2の識別子は2つある。TLS上のh2と、平文TCP上のh2cだ。どちらで話すかはTLSハンドシェイク時のALPN拡張で合意する(TLS側の仕組みはRFC 8446: TLS 1.3を参照)。ただし主要ブラウザはh2しか実装しなかったため、WebにおいてHTTP/2はTLS必須が事実上の標準となった。9113はh2にTLS 1.2以上を要求し、弱い暗号スイートの禁止やSNI必須といった条件も定める。さらに7540にあったHTTP/1.1のUpgradeでh2cへ移行する仕組みは、9113で非推奨になった。
そして多重化しても消えない限界がTCPに残った。TCPは全ストリームを1本の順序保証されたバイト列として運ぶため、パケットが1つ失われると、無関係なストリームのデータまで再送を待って止まる。これがTCPレベルのヘッドオブラインブロッキングで、損失の多い回線ではHTTP/1.1の並列接続に劣る場面すらある。この限界の解消こそ、UDP上のQUICへ移ったHTTP/3(RFC 9114、2022年6月)の直接の動機だ(HTTPのバージョンを参照)。
HTTP/2が解消したのはアプリケーションレイヤーの順番待ちであって、TCPレイヤーの順番待ちではない。この区別がHTTP/3を理解する出発点になる。
つまずきやすい点
まず「多重化したから常に速い」と単純化しないこと。前述の通りTCPレベルのヘッドオブラインブロッキングは残っており、HTTP/2の効果は回線品質に左右される。次に、優先度制御を7540の依存ツリー前提で設計しないこと。あの仕組みは実装が広がらないまま9113で非推奨となり、現在はRFC 9218のurgencyベースが正解だ。最後に、h2cはほぼ使われない。ブラウザは平文HTTP/2に対応せず、Upgradeによる移行も9113で非推奨となったため、h2cの実用例はgRPCのように事前合意できる内部通信にほぼ限られる。
まとめ
RFC 9113は、RFC 9110の意味論を1本のTCP接続で効率よく運ぶフレーミングレイヤーを定義し、7540時代の遺産だった優先度ツリーとUpgrade機構を整理した、現実に合わせた改訂版だ。バイナリフレーム、奇数偶数のストリームID、HPACK、ALPNという部品を押さえれば、HTTP/3との差分整理も一気に楽になる。他のRFC解説はRFC精読から辿れる。
RFC精読の記事ガイド
RFC 9113: HTTP/2を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
RFC
比較で見る軸
難易度: advanced / カテゴリ: RFC精読 / タグ数: 5
導入後に効く点
1本のTCP接続へ識別子付きHEADERS/DATAフレームを混在させて多重化する。ストリームIDはクライアント起点が奇数、サーバー起点が偶数で、HTTP/1.1の複数接続を置換する。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- RFC精読
- タグ数
- 5
判断チェックリスト
- 自社の用途が「RFC / HTTP」に近いか確認する。
- 強みである「RFC 9113(2022年)はRFC 7540(2015年)を置換し、HTTPの意味論を9110へ委譲した。自身はバイナリフレームと多重化に専念し、ヘッダ圧縮はHPACK(7541)を継続する。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。