#このページで確認できること
- ALPN でプロトコルが h2 に決まる様子を
curl -v/openssl s_clientで確認する - 1本のコネクションに複数ストリームが載る多重化をシーケンスで確認する
- ステータスライン・ヘッダーの表現が HTTP/1.1 とどう変わるか(疑似ヘッダー)を確認する
- 観察するヘッダー/項目: ALPN 交渉結果、
:method:path:status、ストリームID
実装上の制約: HTTPS の HTTP/2 と ALPN を検証する場合は、TLS で提供される h2 対応ホストを使用します。TLSなしの h2c は対応サーバーと curl --http2-prior-knowledge などでローカル検証できますが、現在の同梱nginx設定は平文HTTP/1.1です。設定末尾のHTTP/2例はHTTPSでのh2を対象にしています。
#HTTP/1.1 との違い
| HTTP/1.1 | HTTP/2 | |
|---|---|---|
| メッセージ形式 | 開始行・ヘッダーはテキスト、ボディは任意のオクテット列 | バイナリフレーム (HEADERS / DATA / SETTINGS ...) |
| 1コネクションの並列性 | 応答順序に制約。主要ブラウザは通常パイプライン化しない | ストリームによる多重化 |
| ヘッダー | 各メッセージでテキスト形式のフィールドを送信 | HPACK のインデックス表現・リテラル表現で圧縮 |
| プロトコル選択 | HTTPSではALPNの http/1.1、平文HTTPではHTTP/1.1として開始 | HTTPSではALPNの h2。TLSなしのh2cも定義 |
| HoLブロッキング | 応答順序の制約で発生 | HTTPストリーム間に共通の応答順序制約はない。TCPレイヤーには残る |
#ALPN によるプロトコルネゴシエーション
HTTP/2 over TLS では、TLSハンドシェイク中の ALPN (Application-Layer Protocol Negotiation) 拡張でプロトコルを決定します。クライアントが候補リスト(h2, http/1.1)を提示し、サーバーが1つ選びます。TLS 1.3 では、ECHを使わない場合のクライアント提示値は ClientHello で観測できますが、サーバー選択値は暗号化された EncryptedExtensions 内に入ります。curl や openssl s_client はエンドポイントとして復号後の選択結果を表示できます。
participants: Client, Server Client -> Server: TCP 3-way handshake Client -> Server: ClientHello (ALPN offer: h2, http/1.1) Server --> Client: ServerHello (key_share) Server --> Client: EncryptedExtensions (ALPN selected: h2) [encrypted] note: TLS handshake 完了 — 以降は h2 バイナリフレーム Client -> Server: Connection preface (PRI * HTTP/2.0) Client <-> Server: SETTINGS frames Client -> Server: HEADERS frame (stream 1: GET /) Client -> Server: HEADERS frame (stream 3: GET /style.css) Server --> Client: HEADERS + DATA (stream 1) Server --> Client: HEADERS + DATA (stream 3) note: stream 1 と 3 が同一コネクション上で並列に流れる
Upgrade: h2c または事前知識(prior knowledge)で開始します。主要ブラウザでは一般に利用できませんが、gRPCのローカル検証(grpcurl -plaintext)などで登場します。curl では --http2-prior-knowledge で試せます。#ストリーム / フレーム / HPACK
- ストリーム: 1つのリクエスト/レスポンス対に対応する論理的な双方向チャネル。クライアント発は奇数ID (1, 3, 5...)。
- フレーム: 通信の最小単位。
HEADERS(ヘッダー)、DATA(ボディ)、SETTINGS、WINDOW_UPDATE(フロー制御)、RST_STREAM、GOAWAYなど。 - HPACK: ヘッダー圧縮。静的テーブル(61エントリ)+ 動的テーブル + ハフマン符号を使う。動的テーブルに登録された繰り返しフィールドはインデックス参照で表現できるため、条件によっては2回目以降のヘッダーブロックを小さくできる。
- 疑似ヘッダー: リクエストラインが
:method:path:scheme:authorityに分解される。ステータスラインは:status。
#検証コマンド
curl — HTTP/1.1 と HTTP/2 の比較$ curl -I --http1.1 https://example.com/ $ curl -I --http2 https://example.com/ # h2がネゴシエートされた場合、curlの表示は「HTTP/2 200」になる $ curl -v --http2 https://example.com/ 2>&1 | grep -E 'ALPN|HTTP/2' # 「ALPN: server accepted h2」「using HTTP/2」が見えるopenssl — ALPN を直接確認
$ openssl s_client -alpn h2 -connect example.com:443 </dev/null 2>/dev/null \ | grep -i alpn # ALPN protocol: h2 と表示されればサーバーは h2 対応出力例 (curlのバージョンで表示は異なる)
#観測ポイント
| curl | -v で ALPN 交渉結果と疑似ヘッダー(:method 等)を確認。h2cは、対応するローカルサーバーに対して --http2-prior-knowledge で試す。 |
|---|---|
| DevTools | Network の Protocol 列(h2)。同一オリジンへのリクエストが同じ接続に多重化された場合、DevToolsのバージョンによっては2回目以降に新規の Connection Start が表示されない。 |
| Proxy | プロキシが h2 を終端し、上流に HTTP/1.1 で接続する構成もある。クライアント側 h2 / サーバー側 http/1.1 のような非対称構成を意識して観測。 |
| tcpdump / Wireshark | ECHを使わない接続では ClientHello の ALPN 提示値を平文で観測できる(フィルタ: tls.handshake.extensions_alpn_str)。TLS 1.3 のサーバー選択値とHTTP/2フレームの解析には、通常SSLKEYLOGFILEによる復号が必要(フィルタ: http2)。 |
| TLS inspection | インスペクション装置が h2 を通すか、http/1.1 にダウングレードするかは製品差が大きい。ALPN の提示値と選択値を direct / 経由で比較すると経路差を把握しやすい。 |
# ECH未使用時の ClientHello にある ALPN 提示値を抽出(復号不要) tls.handshake.extensions_alpn_str # 復号済みなら HTTP/2 フレームを表示 http2
#次のステップ
HTTP/2 でも TCP パケットロス時には、欠落箇所以降のバイトが届くまで全ストリームの配送が待たされる「TCPレベルの Head-of-line blocking」が残ります。異なるストリーム間の配送停止を避けるために QUIC と HTTP/3 が設計されました。また、HTTP/2 の多重化とtrailersを活用したRPCが gRPC です。