---
title: HTTP/2
url: https://doc.liz6.com/ja/networking/06-HTTP/02-HTTP2
locale: ja
area: networking
tags:
- networking
- HTTP
date: 2026-06-30
modified: 2026-07-16
description: HTTP/2 は1つのTCP接続で複数の並列ストリームを処理する——フレーム多重化、HPACKヘッダー圧縮、サーバープッシュ。HTTP層でのヘッドオブラインブロックは解消されたが、TCP層へ移行した（パケット損失が全ストリームをブロック）。これがHTTP/3がQUICへ移行した理由である。
---

# HTTP/2

> HTTP/2 は1つのTCP接続で複数の並列ストリームを処理する——フレーム多重化、HPACKヘッダー圧縮、サーバープッシュ。HTTP層でのヘッドオブラインブロックは解消されたが、TCP層へ移行した（パケット損失が全ストリームをブロック）。これがHTTP/3がQUICへ移行した理由である。

## 概要

HTTP/2（RFC 7540、2015年）はHTTPのセマンティクス（メソッド、ステータスコード、ヘッダー）を変更せず、トランスポート層を完全に書き換えた：テキストからバイナリフレームへ、1つのTCP接続からマルチプレクストストリームへ、ヘッダーの冗長性からHPACK圧縮へ。目標はHTTP/1.1のパフォーマンスボトルネック——ヘッドオブラインブロックとヘッダーの肥大化——を解決することだった。実装効果は顕著だが、TCP層には依然としてヘッドオブラインブロックが存在する（パケット損失が全ストリームをブロックするため）、この問題を解決するためにHTTP/3はQUICへ移行した。Server Pushは利用率が低いためChromeで削除された。

## HTTP/2の目標

HTTP/1.1のパフォーマンスボトルネックを解決する：

1. ヘッドオブラインブロック：1つのリクエストが遅い → 同じ接続上の全リクエストが待機する
2. ヘッダーの冗長性：各リクエストで大きなヘッダー（Cookie、User-Agent、Accept）が繰り返される
3. 接続数：ブラウザは並列化のために6〜8個のTCP接続を開く（リソースの浪費 + 非決定的な順序付け）

HTTP/2：マルチプレクシング + ヘッダー圧縮 + サーバープッシュ → 1つのTCP接続で全リクエストを効率的に処理。

## バイナリフレーム

HTTP/2はバイナリプロトコルである（テキストではない）：

```
フレーム形式（9バイトのヘッダー + 可変ペイロード）：

Length（3バイト）：ペイロードの長さ ≤ 2^14（16384、デフォルト）または 2^24-1（最大フレームサイズ）
Type（1バイト）：DATA(0x0)、HEADERS(0x1)、PRIORITY(0x2)、RST_STREAM(0x3)、
               SETTINGS(0x4)、PUSH_PROMISE(0x5)、PING(0x6)、GOAWAY(0x7)、
               WINDOW_UPDATE(0x8)、CONTINUATION(0x9)
Flags（1バイト）：タイプに依存
Reserved（1ビット）+ ストリーム識別子（31ビット）：どのストリームか

DATAフレーム：
  フラグ：END_STREAM(0x1)、PADDED(0x8)
  
HEADERSフレーム：
  ペイロード：HPACK圧縮されたヘッダーブロック断片
  フラグ：END_STREAM(0x1)、END_HEADERS(0x4)、PADDED(0x8)、PRIORITY(0x20)

SETTINGSフレーム（接続レベル）：
  ストリーム0でのみ送信：
    SETTINGS_HEADER_TABLE_SIZE (0x1)：HPACK動的テーブルの最大サイズ（デフォルト4096）
    SETTINGS_ENABLE_PUSH (0x2)：0=サーバープッシュ無効化（ほとんどのサーバーが実施）
    SETTINGS_MAX_CONCURRENT_STREAMS (0x3)：最大並列ストリーム数（推奨≥100）
    SETTINGS_INITIAL_WINDOW_SIZE (0x4)：初期フローコントロールウィンドウ（65535）
    SETTINGS_MAX_FRAME_SIZE (0x5)：最大フレームサイズ（16384-16777215）
    SETTINGS_MAX_HEADER_LIST_SIZE (0x6)：最大ヘッダーリストサイズ（バイト）
```

### ストリームマルチプレクシング

```
1つのTCP接続 → 複数のストリーム（クライアント開始：奇数、サーバー開始：偶数）：

ストリーム0：制御フレームのみ（SETTINGS、GOAWAY）
ストリーム1：GET /index.html
ストリーム3：GET /style.css   ← 並列！ストリーム1の完了を待たない
ストリーム5：GET /script.js    ← 並列

各ストリーム：
  - 独立したフローコントロール（ストリームごとのWINDOW_UPDATE）
  - ストリーム1でパケット損失 → TCP再送信 → ストリーム1が停止
    → 他のストリームへの影響：理論的にはなし（フレームレベルでのマルチプレクシング）
    → 実際：TCPパケット損失が全ストリームをブロック（これがHTTP/3が解決しようとしているTCP HOLブロック！）
```

## HPACK

```
HPACK = 静的テーブル（61エントリ）+ 動的テーブル（接続ごと）+ Huffman符号化

静的テーブル（不変）：
  インデックス1：:authority
  インデックス2：:method GET
  インデックス3：:method POST
  インデックス4：:path /
  インデックス5：:path /index.html
  インデックス6：:scheme http
  インデックス7：:scheme https
  インデックス8：:status 200
  インデックス9：:status 204
  ...
  インデックス61：www-authenticate

動的テーブル（FIFO、最大サイズ = SETTINGS_HEADER_TABLE_SIZE）：
  各HEADERSレスポンス終了後 → 動的テーブルに格納（圧縮可能な場合）
  → 後のHEADERSリクエスト → インデックスのみ参照 → 極めて節約

gzip圧縮との比較（gzipを使わない理由）：
  - CRIME攻撃：gzipはヘッダーブロック全体を圧縮 → 圧縮率がCookieの内容を漏洩
  - HPACK：ヘッダーごとの圧縮 → ヘッダー間での圧縮なし → 安全
```

## サーバープッシュ（廃止済み）

```
HTTP/2 サーバープッシュ：
  クライアント：GET /index.html
  サーバー：index.htmlを返す + PUSH_PROMISE（style.css）+ push style.cssデータ
  → クライアントがstyle.cssを事前に受信 → ブラウザの解析 + GETを待たない

廃止された理由：
  - キャッシュ問題：クライアントにstyle.cssが既に存在する可能性あり（キャッシュ）→ サーバープッシュは帯域幅の無駄
  - 実際の利用率が低い：Chromeの統計ではプッシュされたアセットの<2%しか使用されていない
  - 複雑さ：サーバーはクライアントのキャッシュ状態を知らない

Chrome 106以降、サーバープッシュは削除された
```

## 参考

- **RFC**: 7540, 7541, 9113（改訂版）
- **ツール**: `curl --http2 -v`、`nghttp2`、Chrome `chrome://net-internals/#http2`

*キーワード: HTTP/2, マルチプレクシング, HPACK, バイナリフレーム, SETTINGS, ストリーム, フローコントロール, サーバープッシュ*
