HTTP/2とHTTP/3の違い|QUICがWeb表示速度を変える理由
HTTP/2はTCP上のマルチプレクシングで接続数問題を解消したが、パケットロス1%前後でもTCP HoLブロッキングが残る。HTTP/3(QUIC)はストリーム単位で独立させ、初回接続を1 RTT、再訪問を0 RTTまで短縮できる。CDNがHTTP/3対応済みなら有効化コストは低い。ただしUDP 443の疎通確認とLCP/TTFBの計測なしに「速くなった」とは言えない。
HTTP/1.1 から HTTP/3 まで — プロトコル進化の歴史
Webの転送プロトコルは「帯域を増やす」だけでなく、接続あたりの効率を改善する方向に進化してきた。
| 世代 | 年代(概ね) | トランスポート | 主な改善点 |
|---|---|---|---|
| HTTP/1.0 | 1996 | TCP | 1リクエスト1レスポンス。Keep-Aliveは後付け |
| HTTP/1.1 | 1997 | TCP | 永続接続、パイプライン(実運用ではHoLで頓挫) |
| HTTP/2 | 2015(RFC 7540) | TCP | バイナリフレーム、マルチプレクシング、HPACK圧縮 |
| HTTP/3 | 2022(RFC 9114) | QUIC(UDP) | ストリーム単位HoL解消、組み込みTLS 1.3、Connection ID |
HTTP/1.1時代、Chromeは同一ホストへ最大6本のTCP接続しか張れなかった。CSS・JS・フォント・画像が20〜80ファイルに及ぶSPAでは、接続待ちだけで数百msが消える。HTTP/2は1 TCP接続上で数十ストリームを並列化し、このボトルネックを解消した。
しかしHTTP/2の下層はTCPのままだ。TCPは順序保証のため、1パケットのロスがあると後続パケット全体のデリバリが止まる。アプリ層でストリームを分けても、TCPセグメント単位ではHoLが残る。HTTP/3はIETFがQUIC(Quick UDP Internet Connections)を標準化し、トランスポートごと差し替えたのが本質的な転換点だ。
Chromeは2020年頃からHTTP/3を段階的に有効化し、2024年時点で主要CDN(Cloudflare、Fastly、Google)のトラフィックの過半がh3で流れる環境になっている。自社ドメインがCDN経由なら、すでにエッジでHTTP/3が使える可能性が高い。
SPDY — HTTP/2の前身
HTTP/2の原型はGoogleが2012年に公開したSPDY(発音は「スピーディ」)だ。ChromeとGoogleサーバー間で先行実装され、マルチプレクシングとヘッダー圧縮の有効性が実証された。IETFではSPDYをベースにHTTP/2 Working Groupが設立され、2015年にRFC 7540として標準化された。SPDY自体は2016年にChromeから削除されているが、「1接続・多ストリーム」という設計思想はHTTP/3まで受け継がれている。
QUICの標準化タイムライン
| 年 | 出来事 |
|---|---|
| 2012 | GoogleがQUIC prototypeをChromeに実装 |
| 2016 | IETFにQUIC WG設立。Google QUICからIETF QUICへ仕様分離 |
| 2018 | HTTP over QUIC が HTTP/3 として別WG化 |
| 2021 | RFC 9000(QUIC)、RFC 9001(TLS)、RFC 9002(損失復旧) |
| 2022 | RFC 9114(HTTP/3) |
IETF QUICはGoogle QUICとワイヤ互換性がない。nginxの http3 on やCloudflareが提供するのはIETF QUICベースのHTTP/3だ。古い資料で「gQUIC」と書かれている場合は仕様が異なる点に注意する。
HTTP/2 の仕組み — 何が改善し、何が残ったか
HTTP/2の中心機能は次の4つだ。
- バイナリフレーム — テキスト解析のオーバーヘッドを削減
- マルチプレクシング — 1接続で複数リクエスト/レスポンスを同時進行
- HPACK — ヘッダー辞書圧縮(Cookie肥大化対策)
- Server Push — サーバー主導プッシュ(2023年以降は非推奨・削除方向)
典型的なECサイトの初回ロードでは、HTML 1本 + CSS 3本 + JS 8本 + フォント 2本 + 画像 15本 = 29リクエストが同一オリジンへ飛ぶ。HTTP/1.1では6接続×順次処理で、最遅リソースの待ちが支配的だった。HTTP/2では1接続で並列化され、Waterfallの「階段」が短くなる。
ただしHTTP/2はTCP + TLSの接続確立コストをそのまま引き継ぐ。TLS 1.2時代は TCP 3-way(1.5 RTT)+ TLS handshake(2 RTT)= 合計3.5 RTT が初回接続の最低ラインだった。TLS 1.3で1 RTTに短縮されても、TCP自体のHoLは解決しない。
フレーム種別とストリーム優先度
HTTP/2の通信単位はフレームだ。主要なタイプは次のとおり。
- DATA — リクエスト/レスポンスボディ
- HEADERS — 圧縮済みHTTPヘッダー(HPACK)
- SETTINGS — 接続パラメータ交換(最大同時ストリーム数など)
- WINDOW_UPDATE — フロー制御ウィンドウ更新
- RST_STREAM / GOAWAY — ストリーム/接続の異常終了
各リクエストはストリームID(奇数=クライアント発、偶数=サーバー発)で識別される。HTTP/2にはストリーム依存関係(Dependency Tree)とWeightによる優先度制御があったが、ブラウザ実装の差異が大きく、2023年以降はExtensible Priorities(RFC 9218)へ移行が進んでいる。優先度制御はLCP要素(HTML・CSS)を先に届ける理論上の手段だが、TCP HoLが発生すると優先度以前に全ストリームが止まるのがHTTP/2の構造的限界だ。
Server Push の終焉
HTTP/2 Server Pushは「HTMLより先にCSS/JSを押し付ける」機能だったが、キャッシュと競合しやすく、押されたリソースの半分以上が未使用になるという計測結果が報告された。Chromeは2022年に削除、nginxも http2_push off がデフォルト。HTTP/3でもPushは削除方向で、代わりに <link rel="preload"> や103 Early Hints(CRP最適化参照)が推奨される。
- 効きやすい — 同一オリジンへのリソース数が多い(20ファイル超)、HTTP/1.1時代にドメインシャーディングしていたサイト
- 効きにくい — リソースが5ファイル以下のLP、すでにHTTP/2 + CDN + 積極的キャッシュでTTFB 200ms以下のサイト
TCP ヘッドオブラインブロッキング — 図解と数値感
ヘッドオブラインブロッキング(Head-of-Line Blocking, HoL) とは、キュー先頭の処理が遅延すると、後続の処理すべてが待たされる現象だ。HTTP/2では「アプリ層のHoL」は解消されたが、「TCP層のHoL」は残る。
具体例:3ストリーム、1パケットロス
同一TCP接続上で Stream A(HTML)、Stream B(CSS)、Stream C(JS)を並列ダウンロード中、Stream Aのパケット#3がロストしたとする。
送信側 TCP 受信側 TCP
───────── ─────────
[A1][A2][A3×lost][A4][B1][B2] → A1 OK, A2 OK, A3 未到着
A4 到着 → 順序が飛んでいるため保留
B1 到着 → 保留(A3待ち)
B2 到着 → 保留(A3待ち)
… A3 再送完了まで B1/B2 もアプリに渡せない
Stream BとCはHTTP/2的には独立しているが、TCPはバイトストリーム1本として扱う。ロスしたセグメントより後ろのデータは、再送(RTO、典型値200ms〜1s)が完了するまでアプリケーション層へ上がらない。
パケットロス率と体感
| 環境 | 典型RTT | パケットロス率 | HTTP/2への影響 |
|---|---|---|---|
| 同一AZ内(AWS等) | 0.5〜2ms | 0.01%未満 | ほぼ無視できる |
| 家庭の光回線 | 15〜40ms | 0.1〜0.5% | 大きなファイル転送時に稀にスパイク |
| 4G/LTE | 50〜120ms | 1〜3% | 並列リソース読み込みで顕著 |
| 移動中の5G handover | 80〜200ms | 2〜5% | 接続断・再確立コストも加算 |
ロス率1%、RTT 100msの回線で50KBのCSSを6ストリーム並列取得すると、HTTP/2では1ストリームのロスが他5ストリームを100〜300ms止めることがある。HTTP/3(QUIC)ではロストしたストリームだけ再送し、他ストリームは進行する。
TCP RTO(再送タイムアウト)の概算
TCPはパケットロスを検知するとRTO(Retransmission Timeout)後に再送する。Linuxの典型初期RTOは 1秒( 等で変動)。RTT 100ms環境でも、ACKが返らない場合は最低200ms〜1s待ってから再送が走る。
待ち時間 ≈ RTT × 2〜4(Fast Retransmit前) または RTO(1s級)
6ストリーム並列中に1パケットロス → 全ストリームが上記待ちに巻き込まれる。これが「Wi-Fiが悪いと突然ページが固まる」体感の正体だ。QUICはパケット番号ベースの損失検知とPer-Stream再送で、この待ちをストリーム単位に閉じ込める。
HTTP/1.1 vs HTTP/2 vs HTTP/3 のHoL比較
| レイヤ | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| アプリ層HoL | あり(パイプライン) | 解消 | 解消 |
| TCP層HoL | あり | あり | なし(UDP) |
| ストリーム独立性 | 接続単位 | TCP上の論理分割 | QUICネイティブ |
上記は同一ページ(29リソース、合計1.2MB)を4G相当(RTT 100ms、ロス2%)でシミュレーションしたLCP近似値(ms)のイメージだ。実測値はリソースサイズ・CDN・キャッシュ状態で大きく変わる。重要なのは「HTTP/2はロスゼロなら最速クラスだが、ロスがあると優位性が薄れる」という関係性だ。
QUIC と HTTP/3 — トランスポート層の再設計
QUICはUDP上に構築された信頼性付きマルチプレクストリームプロトコルだ。HTTP/3はQUICの上でHTTPセマンティクス(メソッド、ヘッダー、ステータスコード)を載せる。
QUICの設計上の要点:
- ストリーム独立性 — 各ストリームに個別のフロー制御・再送キュー。1ストリームのロスが他に波及しない
- TLS 1.3の組み込み — 暗号化がプロトコル仕様の一部。平文のQUICは存在しない
- Connection ID(CID) — 5タプル(送信元/先IP・ポート・プロトコル)ではなくCIDで接続を識別
- ユーザー空間実装 — OSカーネルのTCPスタックを経由せず、Chrome/nginx等がスタックを更新できる
HTTP/3のワイヤフォーマットはHTTP/2のフレーム概念を引き継ぐが、TCPの代わりにQUICストリームを使う。ALPN(Application-Layer Protocol Negotiation)の値は h3(HTTP/3)だ。
ブラウザは通常、最初の接続をHTTP/2(TCP 443)で行い、レスポンスの Alt-Svc: h3=":443"; ma=86400 ヘッダーを見て次回以降HTTP/3を試行する。初回アクセスだけh2のまま終わることもあるため、計測は2回目以降のキャッシュクリア付きリロードも含めて行う。
QUICパケット構造(概念)
1つのUDPペイロード内に複数のQUICフレームが入る。
- CRYPTO — TLS 1.3ハンドシェイクメッセージ
- STREAM — HTTP/3のリクエスト/レスポンスデータ
- ACK — 受信確認(TCPのACKに相当するが、パケット番号空間が独立)
- CONNECTION_CLOSE — 接続終了理由
Connection IDは通常8〜20バイト。NATが内部IPを書き換えても、CIDが一致すれば同一接続として扱える。これがコネクションマイグレーションの土台だ。
損失復旧:TCP vs QUIC
TCPは順序番号(sequence number)で全バイトストリームを管理する。1バイト分の欠損でも後続全体が止まる。
QUICはパケット番号とストリーム内オフセットを分離管理する。パケット#100がロストしても、パケット#101に含まれるStream BのデータはStream Bの再送キューだけに影響する。Stream Aのデータが同じパケット#100に入っていた場合のみStream Aが再送待ちになる — HTTP/2の「無関係ストリームまで巻き込む」問題より粒度が細かい。
TLS 1.3 と 0-RTT — 接続確立の短縮
接続確立の遅延はTTFB(Time To First Byte)に直結する。クリティカルレンダリングパスの最上流にあるのが「HTMLが届くまでの時間」だ。
ハンドシェイクのRTT比較
| 構成 | 初回接続 | 再訪問(セッション再利用) |
|---|---|---|
| HTTP/1.1 + TLS 1.2 | 3.5 RTT | 2 RTT(Session Resumption) |
| HTTP/2 + TLS 1.3 | 2 RTT(1-RTT) | 1 RTT |
| HTTP/3 + TLS 1.3 | 1 RTT | 0 RTT(Early Data) |
RTT 80msの4G回線で初回接続だけ見ると、HTTP/2(TLS 1.3)で160ms、HTTP/3で80ms — TTFBだけで80msの差が理論上出る。再訪問の0-RTTでは、クライアントがハンドシェイク完了前にGETリクエストを送れるため、サーバー処理時間 + 1 RTT分だけ短縮される。
0-RTTの制約
0-RTTデータはネットワーク上でリプレイ(再送)されうる。攻撃者がキャプチャした0-RTT GETを再送してもサーバーが再度処理してしまうリスクがある。
実務上の扱い:
- GET / HEAD / 静的アセット — 0-RTT許可の候補
- POST / PATCH / 決済 / ログイン — 0-RTT禁止(
ssl_early_data offまたは CDN側で無効化) - Cache-Control — 0-RTTレスポンスはCDNキャッシュ戦略と整合させる
Cloudflareでは「ゼロRTT復元」がダッシュボード設定。nginxでは ssl_early_data on; を明示しない限りOFF。本番で有効化する前に、状態変更APIがEarly Data経路に乗らないことを確認する。
コネクションマイグレーション — モバイルシナリオ
スマートフォンは通勤中に Wi-Fi → 4G/5G → 駅のWi-Fi とネットワークを切り替える。TCP/TLSではIPアドレスが変わると5タプルが変わり、接続は切断され再ハンドシェイクが必要だ。
シナリオ:動画サムネイル付きニュースアプリ
- ユーザーがWi-Fi(192.168.1.10)で記事一覧を読み込み中
- 30秒後、電波状況悪化で4G(10.20.30.40)へフェイルオーバー
- HTTP/2 — TCP接続断 → 新規3-way + TLS → 未完了リクエスト再送 → 300〜800msの空白
- HTTP/3 — Connection IDは維持、新IPへQUICパケットを送り続け → 50〜150ms程度の切替(パス MTU 変更の調整含む)
QUICのConnection IDはサーバーとクライアントが協調して更新できる(CID rotation)。NATリバインディングやロードバランサ背後でも、「同じ論理セッション」として扱えるのがHTTP/3の実務上の強みだ。
モバイルユーザー比率が60%超のメディア・EC・SNSでは、ページ離脱率(bounce rate)改善に直結するケースがある。ただしアプリ内WebViewが古いOSだとHTTP/3非対応のこともあり、User-Agent分布の確認は必須だ。
数値シミュレーション:ネットワーク切替コスト
| イベント | HTTP/2(TCP+TLS1.3) | HTTP/3(QUIC) |
|---|---|---|
| IP変更検知〜再接続 | 即座にRST | CID維持、継続送信 |
| 新接続ハンドシェイク | 1 RTT(80ms@4G) | 不要(0 RTT) |
| 未完了リクエスト再送 | 全ストリーム再開 | 中断ストリームのみ |
| 合計ダウンタイム目安 | 200〜600ms | 30〜100ms |
動画のチャンク配信やWebSocket over HTTP/3では、この差がバッファリングや切断に直結する。静的HTML中心のブログでは体感差は小さいが、API呼び出しが多いSPAでは切替中のAPI失敗(net::ERR_NETWORK_CHANGED)がHTTP/3で減る報告もある。
Cloudflare CDN で HTTP/3 を有効化する
自前オリジンをHTTP/2のまま維持し、CDNエッジでHTTP/3を終端するのが最もコストの低い導入パターンだ。
ダッシュボード設定
- Cloudflareダッシュボード → 対象ゾーン → Network
- HTTP/3 (with QUIC) を ON
- 必要に応じて 0-RTT Connection Resumption を ON(APIのリプレイリスクを評価した上で)
- WebSockets over HTTP/3 は用途に応じて(通常のHTML/CSS/JS配信では無関係)
Cloudflare経由の場合、オリジンへの通信はHTTP/2またはHTTP/1.1のままでよい。エッジが Alt-Svc を付与し、クライアントとの間だけQUICになる。
オリジン側nginx(HTTP/3直結の場合)
CDNを使わずnginxで直接HTTP/3を終端する最小構成:
server {
listen 443 quic reuseport;
listen 443 ssl;
http2 on;
http3 on;
ssl_certificate /etc/ssl/certs/example.com.pem;
ssl_certificate_key /etc/ssl/private/example.com.key;
ssl_protocols TLSv1.3;
# 0-RTTはGET中心の静的サイトのみ検討
ssl_early_data on;
add_header Alt-Svc 'h3=":443"; ma=86400' always;
location / {
root /var/www/html;
try_files $uri $uri/ =404;
}
}
reuseport は複数ワーカープロセスがUDP 443を分担するために推奨される。ファイアウォールで UDP 443/TCP 443 両方を開放する。ma=86400 はAlt-Svcの有効期間(秒)で、86400 = 24時間。
curl で HTTP/3 ネゴシエーションを確認
# HTTP/3専用で接続(失敗時はエラー終了)
curl --http3-only -sI https://example.com/ | grep -iE '^(HTTP/|alt-svc:)'
# 期待出力例:
# HTTP/3 200
# alt-svc: h3=":443"; ma=86400
# HTTP/2とのTTFB比較(5回平均の目安取り)
for i in 1 2 3 4 5; do
curl --http2 -s -o /dev/null -w "h2 ttfb=%{time_starttransfer}s\n" https://example.com/
curl --http3-only -s -o /dev/null -w "h3 ttfb=%{time_starttransfer}s\n" https://example.com/
done
curl は --with-ngtcp2 --with-nghttp3 ビルドが必要。Windowsでは curl.exe(Windows 11 22H2以降)またはWSL上のcurl 8.xを使う。
Cloudflare Workers / Pages との関係
Cloudflare WorkersやPagesにデプロイした静的サイトも、自動的にエッジHTTP/3の対象になる。追加コードは不要だ。Workers自体がfetch APIで外部へHTTP/3接続する場合、Cloudflareのネットワーク内部でQUICが使われるが、オリジンfetchのプロトコル選択はCloudflare側に委ねられる点は変わらない。
キャッシュヒット率が90%超のサイトでは、オリジンTTFBよりエッジからクライアントへの転送が支配的になる。HTTP/3の効果は「キャッシュミス時の初回HTML取得」と「多数の非キャッシュAPI呼び出し」で出やすい。
Alt-Svc とキャッシュの挙動
Alt-Svc ヘッダーはブラウザがメモリ + ディスクキャッシュに保存する。ma=86400 は24時間HTTP/3を優先試行する意味だ。CDNを切り替えた直後は古いAlt-Svcが残り、一時的にh3接続失敗→h2フォールバックが増える。DNS TTL切替後24時間は両方のプロトコルで計測を続けるのが安全だ。
LCP・TTFB で効果を計測する
「HTTP/3にした」だけでは改善証明にならない。TTFB(HTML到着) と LCP(最大コンテンツ描画) を、同条件でHTTP/2と比較する。
計測指標の読み方
| 指標 | 意味 | HTTP/3が効きやすい条件 |
|---|---|---|
| TTFB | リクエスト送信〜最初のバイト受信 | 初回接続、キャッシュなし、RTT高 |
| LCP | 最大コンテンツ要素の描画完了 | 多数リソース + ロス率高 + モバイル |
| FCP | 最初のコンテンツ描画 | CRP最適化とセットで見る |
LCP改善の本丸は依然としてクリティカルレンダリングパスの最適化(レンダーブロッキングCSS削減、LCP画像のpreload、フォント最適化)だ。HTTP/3はネットワーク層の底上げであり、2.5MBの未圧縮画像を載せたままではLCPは改善しない。
Chrome DevTools でプロトコル確認
DevTools → Network パネル → 列ヘッダー右クリック → Protocol を表示。h3 / h3-29 / h2 / http/1.1 が確認できる。
キャッシュの影響を除く検証手順:
- DevToolsを開いた状態で Disable cache にチェック
- Hard Reload(Ctrl+Shift+R)を2回実行(2回目でAlt-Svc経由のh3が出やすい)
- ドキュメント行(HTML)の Protocol 列を確認
コマンドライン計測(Lighthouse CI / WebPageTest)
# Lighthouse(モバイル回線シミュレーション、TTFB/LCP出力)
npx lighthouse https://example.com/ \
--preset=desktop \
--only-categories=performance \
--output=json \
--output-path=./lh-h3.json \
--chrome-flags="--enable-quic"
# JSONから主要指標を抽出(jq使用)
jq '.audits["server-response-time"].displayValue, .audits["largest-contentful-paint"].displayValue' ./lh-h3.json
WebPageTestでは Advanced → Chromium → HTTP/3 を有効にし、同一URLを HTTP/2 only と HTTP/3 で2回テストして Start Render / LCP / TTFB を並べる。テストロケーションは 4G Tokyo / Mumbai などターゲットユーザーに近いものを選ぶ。
上記はあるECサイト(CDN + 画像最適化済み)のBefore/After例(ms)。TTFB 115ms短縮、LCP 750ms短縮。ただし自サイトで再現性を確認する前提の参考値だ。
フィールドデータ(RUM)での確認
Lab計測(Lighthouse)だけでは、ユーザーの実回線を反映できない。Real User Monitoring で navigation.responseStart - navigation.requestStart をTTFB近似値として集計し、プロトコル別(nextHopProtocol)に分割する。
// PerformanceNavigationTiming から TTFB とプロトコルを取得
const [entry] = performance.getEntriesByType('navigation');
const ttfb = entry.responseStart - entry.requestStart;
const protocol = entry.nextHopProtocol; // "h2", "h3", "http/1.1" 等
console.log({ ttfb: Math.round(ttfb), protocol });
Google Analytics 4 や Datadog RUM、Cloudflare Web Analytics では nextHopProtocol をカスタムディメンションとして送れる。HTTP/3比率が70%以上なのにTTFB中央値が改善しない場合、オリジン側かアプリ処理がボトルネックの可能性が高い。
Core Web Vitals との関係
GoogleのランキングシグナルはLCP・INP・CLSだ。HTTP/3はLCPとTTFBに間接的に効くが、INP(Interaction to Next Paint) や CLS には直接効かない。プロトコル変更後にLCPだけ改善してINPが悪化するケース(JS実行時間増)はないか、合わせて確認する。
UDP 443 がブロックされる — トラブルシューティング
QUICはUDP 443を使う。企業プロキシ、古いルーター、厳格なセキュリティポリシーではUDP全体または443/UDPだけDROPされる。
症状
- Chrome DevToolsで常に
h2のまま。h3に切り替わらない curl --http3-onlyがタイムアウト、curl --http2は成功- 社内ネットワークのみ再現、自宅回線では
h3が出る
疎通確認コマンド
# UDP 443 到達性(nc が UDP をサポートする環境)
nc -zuv example.com 443
# QUIC ハンドシェイクの詳細(quiche-client / aioquic 等の専用ツール)
# Cloudflareの場合、公式の http3 テストページも参照できる
# TCP 443 は通るが UDP 443 だけ落ちるケースの切り分け
curl -sI --http2 https://example.com/ | head -1 # → HTTP/2 200
curl -sI --http3-only https://example.com/ # → タイムアウト or 接続拒否
対処方針
| 状況 | 対処 |
|---|---|
| 自社FW | UDP 443 を許可。ダメならHTTP/2フォールバックに任せる(ユーザー影響なし) |
| CDN設定ミス | Alt-Svc が返っているか、エッジでHTTP/3がONか確認 |
| 特定ISP | 報告はCDN事業者側の問題。自前サーバーでは制御不能 |
| 0-RTT起因の不具合 | Early DataをOFFにして再計測 |
HTTP/3はGraceful fallbackが仕様上想定されている。UDPが使えなければTCPのHTTP/2で動く。ページが表示されない致命的事象は稀で、最適化の恩恵が得られないのが主な損失だ。
セキュリティヘッダーの Strict-Transport-Security と併用する場合、HTTPS強制はTCP/UDP両方に有効。HSTS Preload 登録済みなら初回HTTPアクセスも削減でき、結果的にTTFB改善に寄与する。
企業ネットワークでよくあるブロックパターン
- Stateful FW — UDPセッション状態を追跡せず全DROP。TCP 443のみ許可
- SSL Inspection Proxy — 中間者証明書でTLS復号。QUICは復号不能なためUDP 443を遮断
- 古いCisco ASA — QUIC/UDP443の明示Denyルールがテンプレートに残存
- 公衆Wi-Fi Captive Portal — UDPがポータル認証前にブロック
対策として、社内向けヘルプデスクに「UDP 443 outbound allow」の依頼テンプレートを用意する。ユーザー影響はフォールバックで吸収されるが、モバイルVPN経由の社員はHTTP/3恩恵がゼロになる。
nginx ログでのプロトコル確認
log_format quic '$remote_addr - [$time_local] "$request" '
'$status $body_bytes_sent '
'proto=$protocol rt=$request_time';
access_log /var/log/nginx/access.log quic;
$protocol が HTTP/3.0 を返していればクライアントはh3接続成功。HTTP/2.0 のみならAlt-Svc未到達かUDPブロックを疑う。
HTTP/2 vs HTTP/3 — 比較表
| 観点 | HTTP/2 / HTTP/3 |
|---|---|
| トランスポート | HTTP/2: TCP / HTTP/3: QUIC(UDP) |
| マルチプレクシング | 両方対応。HTTP/3のみストリーム単位でTCP HoLを回避 |
| 暗号化 | HTTP/2: TLS(別レイヤ) / HTTP/3: TLS 1.3組み込み |
| 初回接続 | HTTP/2: 1-RTT(TLS1.3) / HTTP/3: 1-RTT |
| 再訪問 | HTTP/2: 1-RTT / HTTP/3: 0-RTT(設定次第) |
| ネットワーク切替 | HTTP/2: 再接続必須 / HTTP/3: Connection IDで継続可能 |
| ファイアウォール | HTTP/2: TCP 443のみ / HTTP/3: UDP 443も必要 |
| サーバー負荷 | HTTP/3: UDP処理・userspace実装でCPUやや増(CDNが吸収) |
| ブラウザ対応 | HTTP/2: 99%+ / HTTP/3: Chrome・Firefox・Safari 16+(2024〜) |
| 導入コスト | CDN利用ならダッシュボード1クリック。自前はnginx 1.25+とFW変更 |
本番導入チェックリスト
CDNまたはnginxでHTTP/3(QUIC)が有効か、Alt-Svc: h3 ヘッダーが返るか確認する
ファイアウォール・セキュリティグループで UDP 443 / TCP 443 両方を許可する
curl --http3-only と Chrome DevTools の Protocol 列で h3 ネゴシエーションを検証する
0-RTTを有効にする場合、POST/決済/ログインAPIがEarly Data経路に乗らないことを確認する
WebPageTest / Lighthouse で TTFB・LCP を HTTP/2 環境と同一条件で比較する
社内ネットワーク・モバイル実機で UDP ブロック時の HTTP/2 フォールバックを確認する
改善幅が10%未満なら [CRP最適化](/blog/critical-rendering-path-最適化/) やキャッシュ設計を優先する
nginx / CDN 設定後の確認ポイント
- レスポンスヘッダーに
alt-svc: h3=":443"があるか curl --http3-only -w '%{http_version}\n'が3を返すか- サーバーログ(nginx
$protocol変数)にHTTP/3.0が記録されるか - CPU使用率の増加が許容範囲か(通常5〜15%程度の増は珍しくない)
HTTP/3はネットワーク層の最適化だ。APIの ETag / Cache-Control 設計、画像圧縮、レンダーブロッキング削減と組み合わせて初めてLCP 2.5秒以内が現実的になる。プロトコル変更だけでCore Web Vitalsが劇的に改善するケースは、もともとネットワークボトルネックが支配的だったサイトに限られる。
よくある誤解 — HTTP/3神話の解体
| 誤解 | 実際 |
|---|---|
| HTTP/3にすればLCP必ず2秒切り | 画像・JS・フォント最適化なしでは限界がある |
| HTTP/2はもう不要 | UDPブロック環境ではHTTP/2が実体。両方維持が前提 |
| 0-RTTは無条件でON | リプレイリスクあり。API設計とセットで判断 |
| QUIC=暗号化なしUDP | IETF QUICはTLS 1.3必須。平文QUICは存在しない |
| CDN ONだけで計測不要 | Alt-Svc未到達・UDPブロックでh2のままのユーザーが残る |
関連記事
この記事は Webパフォーマンス テーマの一環です。あわせて読むと理解が深まる関連記事をまとめました。
| トピック | 記事 |
|---|---|
| Speculation Rules API 完全ガイド | Speculation Rules API 完全ガイド|prefetch・prerender・Chrome 144 prerender_until_script |
| fetchpriority属性でLCP画像を最適化する | fetchpriority属性でLCP画像を最適化する|2026年実践ガイド |
| Web Vitals INP改善ガイド | Web Vitals INP改善ガイド|scheduler.yield・Long Task・Input Delay対策(2026) |
| content-visibility: auto 完全ガイド | content-visibility: auto 完全ガイド|遅延レンダリングと長尺ページ最適化 2026 |
| Server-Timing API 完全ガイド | Server-Timing API 完全ガイド|Express ミドルウェアと Chrome DevTools 連携 |
| Brotli vs Gzip 圧縮比較 | Brotli vs Gzip 圧縮比較|nginx静的・動的設定とCDN実践ガイド |
| CSS/JSを圧縮(Minify)するメリット | CSS/JSを圧縮(Minify)するメリット|サイト高速化の基本 |
| Service Worker キャッシュ戦略ガイド | Service Worker キャッシュ戦略ガイド|PWA の cache-first・network-first・SWR・Workbox 設計 |
| View Transitions API 完全ガイド | View Transitions API 完全ガイド|SPA・MPA ページ遷移と Astro/React 統合 |
| WebP変換で画質が落ちる原因と最適な設定 | WebP変換で画質が落ちる原因と最適な設定 |
| 画像圧縮とWebP変換 | 画像圧縮とWebP変換|PageSpeed改善の基本 |
| HTTPSレコードとSVCBレコードの完全解説 | HTTPSレコードとSVCBレコードの完全解説|ALPN・ECH・HTTP/3のDNS設定 |
まとめ
HTTP/2はHTTP/1.1の接続数問題とヘッダー冗長を解消したが、TCPの順序保証ゆえにパケットロス時のHoLは残った。HTTP/3はQUICでストリームを独立させ、1 RTT接続・0-RTT再訪問・コネクションマイグレーションまで実現する。
導入の現実解は、Cloudflare等のCDNでHTTP/3をONにし、curl --http3-only と DevTools で h3 を確認し、TTFB/LCPを計測することだ。UDP 443が塞がれていてもHTTP/2へフォールバックするため、リスクは低い。一方、改善幅は回線品質とサイト構成に依存する — 数値で確認し、効果が薄ければCRP最適化とキャッシュ設計に工数を回すのがエンジニアリングとして正しい順序だ。
次のアクションとして、本番ドメインで curl --http3-only -sI https://your-domain/ を実行し、HTTP/3 200 が返るかを確認するところから始めてほしい。返らなければCDN設定かUDP 443の疎通を、返ればWebPageTestで4Gロケーション計測を回し、TTFB/LCPの差分を記録する。