目次
- HTTP/1.1の限界:HoLブロッキングとは何か
- 1本の接続で同時に1リクエストしか処理できない
- HTTP/2の多重化:1本のTCP接続でストリームを並列化
- バイナリフレームとストリーム
- それでも残る「TCPレベルのHoLブロッキング」
- HTTP/3とQUIC:UDPの上で「詰まらない接続」を作り直す
- なぜTCPを改良せずUDPを選んだのか
- QUICが解決する3つのこと
- 3バージョンの比較
- 対応状況と自サイトで有効化して確認する手順
- ブラウザとCDNはほぼ対応済み
- NginxはHTTP/2が標準、HTTP/3は1.25系以降
- Nginxでの設定例
- curlで確認する
- ブラウザの開発者ツールで確認する
- トラブル事例:有効化したのにh3にならない
- 症状
- 原因
- 対処
- 実務での判断:HTTP/3にどこまで投資するか
- 効果が出やすい条件と出にくい条件
- まとめ
「CDNの管理画面にHTTP/3のスイッチがあるけれど、オンにして何が変わるのか説明できない」「開発者ツールのProtocol列にh2やh3と出ているが、その違いが性能にどう効くのかわからない」。Webアプリを作れるエンジニアでも、HTTPのバージョン差は意外と曖昧なまま運用していることが多いものです。
この記事では、HTTP/1.1が抱えていた課題からHTTP/2の多重化、そしてHTTP/3がQUICというUDPベースの新しいトランスポートで何を解決したのかを仕組みから整理します。あわせて、Nginx・CDN・ブラウザの対応状況と、自分のサイトでHTTP/3を有効化して確認するまでの手順、有効化したのに使われない典型的なトラブルの切り分け方までを扱います。TCP/IPやHTTPそのものの基礎はエンジニアのためのネットワーク基礎で解説しているので、本記事はその先の「バージョン差」に集中します。
HTTP/1.1の限界:HoLブロッキングとは何か
1本の接続で同時に1リクエストしか処理できない
HTTP/1.1では、1本のTCP接続の上で「リクエストを送り、レスポンスを受け取る」というやり取りを順番に行います。複数のリクエストを先行して送るパイプライン機能は仕様上ありますが、レスポンスは必ず送った順に返さなければならず、先頭の重いレスポンスが後続を全部待たせてしまいます。この「先頭が詰まると後ろが進めない」現象をHoL(Head-of-Line)ブロッキングと呼びます。
ブラウザはこれを回避するために、同じホストに対して複数(一般的なブラウザでは6本前後)のTCP接続を並列に張ります。しかし接続ごとにTCPの3ウェイハンドシェイクとTLSハンドシェイクが必要になり、しかも各接続が独立して輻輳制御を行うため帯域の奪い合いも起きます。画像やJSを1つのファイルに結合する、ドメインを分けて接続数を増やすといったHTTP/1.1時代の最適化テクニックは、すべてこの制約に対する回避策でした。
HTTP/2の多重化:1本のTCP接続でストリームを並列化
バイナリフレームとストリーム
HTTP/2(RFC 9113)は、テキストベースだったHTTP/1.1のメッセージをバイナリの「フレーム」に分割し、1本のTCP接続上に複数の「ストリーム」を多重化します。各フレームにはストリームIDが付いているため、リクエストAのレスポンスの途中にリクエストBのレスポンスを差し込んでも、受信側で正しく組み立て直せます。これにより、アプリケーション層でのHoLブロッキングは解消され、接続は原則1本で済むようになりました。
さらにHPACKというヘッダー圧縮により、毎回同じCookieやUser-Agentを送る無駄が減ります。サーバープッシュという機能もありましたが、効果が限定的でChromeでは既に無効化されており、実務では使わない前提で構いません。
それでも残る「TCPレベルのHoLブロッキング」
HTTP/2にも弱点があります。多重化はあくまでHTTPの層の話で、その下はやはり1本のTCP接続です。TCPは「バイト列を順序どおり、欠けなく届ける」プロトコルなので、パケットが1つ失われると、そのパケットが再送されて届くまで、後続のバイト列(別ストリームのデータも含む)はアプリケーションに渡されません。つまり、あるストリームのパケットロスが無関係なストリームまで止めてしまいます。
回線品質の良い有線環境では目立ちませんが、パケットロスが起きやすいモバイル回線ではこの影響が大きく、条件によってはHTTP/1.1の複数接続より遅くなるケースすら報告されていました。この「TCPレベルの詰まり」がHTTP/3を生む動機になります。
HTTP/3とQUIC:UDPの上で「詰まらない接続」を作り直す
なぜTCPを改良せずUDPを選んだのか
TCPの改良は、OSカーネルや経路上のルーター・ファイアウォールなど、世界中の機器の更新を伴うため現実的に進みません。そこでQUIC(RFC 9000)は、ほとんどの機器がそのまま通すUDPの上に、信頼性・順序制御・輻輳制御をユーザー空間で実装し直すという設計を取りました。HTTP/3(RFC 9114)は、このQUICの上でHTTPのやり取りを行うプロトコルです。TCPとUDPの性質の違いはTCPとUDPの違いと使い分けで詳しく整理しています。
QUICが解決する3つのこと
第一に、ストリームごとに独立した再送制御です。QUICではパケットロスの影響がそのストリームだけに閉じるため、TCPレベルのHoLブロッキングが起きません。第二に、ハンドシェイクの短縮です。QUICはTLS 1.3を内蔵しており、TCPの接続確立とTLSの鍵交換を別々に行っていた従来と違い、1往復(1-RTT)で暗号化通信を開始できます。再接続時には0-RTTでデータ送信まで可能です。第三に、コネクションマイグレーションです。TCPの接続はIPアドレスとポートの組で識別されるため、Wi-Fiからモバイル回線に切り替わると接続が切れますが、QUICは「コネクションID」で接続を識別するため、IPが変わっても通信を継続できます。
ヘッダー圧縮はHPACKの順序依存問題を避けるためQPACKに置き換えられました。ユーザーから見ると「モバイルで速くなる、切り替えに強い、最初の1バイトが早く届く」という体験の差になります。
3バージョンの比較
| 項目 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| トランスポート | TCP | TCP | QUIC(UDP) |
| 多重化 | なし(複数接続で代替) | ストリームで多重化 | ストリームで多重化 |
| HoLブロッキング | HTTP層で発生 | TCP層で発生 | 発生しない |
| 暗号化 | 任意(TLS) | 事実上必須(ブラウザはTLSのみ) | 必須(TLS 1.3内蔵) |
| 接続確立 | TCP+TLSで2〜3往復 | TCP+TLSで2〜3往復 | 1往復、再接続は0-RTT |
| ヘッダー圧縮 | なし | HPACK | QPACK |
| IP変更時 | 切断 | 切断 | 継続(コネクションID) |
対応状況と自サイトで有効化して確認する手順
ブラウザとCDNはほぼ対応済み
Chrome、Firefox、Safari、Edgeの主要ブラウザは、いずれもHTTP/3に対応しています。CDNもCloudflare、AWS CloudFront、Fastly、Akamaiなど主要どころは対応済みで、管理画面のトグルを有効にするだけでエッジとブラウザ間がHTTP/3になります。CDNを前段に置いている構成なら、オリジンサーバーを触らずにHTTP/3の恩恵を受けられるのが現実的な近道です。CloudFrontの設定はAWS CloudFrontでサイト高速化で扱っています。
一方、注意したいのはロードバランサーです。AWSのALBはHTTP/2には対応していますが、HTTP/3(QUIC)の終端には対応していません(執筆時点)。「ALBの後ろのNginxでHTTP/3を有効にしたのに効かない」という相談は、そもそもALBがUDPを終端できないことが原因です。HTTP/3を使いたいならCDNをALBの前に置くか、QUIC対応のロードバランサーを選ぶ必要があります。
NginxはHTTP/2が標準、HTTP/3は1.25系以降
NginxのHTTP/2はずっと以前から安定機能で、1.25.1以降はlisten行のhttp2パラメータに代わり、http2 on;ディレクティブで有効化する形が推奨されています。HTTP/3は1.25.0で本体に取り込まれ、その後の安定版でも利用できますが、ディストリビューションのパッケージがビルド時にQUICを含んでいるかは確認が必要です。nginx -Vの出力に--with-http_v3_moduleがあれば使えます。
Nginxでの設定例
Nginxの基本構築とSSL化が済んでいる前提(Linux×Nginx入門参照)で、HTTP/2とHTTP/3を同時に有効化する最小構成は次のとおりです。
server {
# TCP側(HTTP/1.1とHTTP/2)
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
# UDP側(HTTP/3)
listen 443 quic reuseport;
listen [::]:443 quic reuseport;
http3 on;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# ブラウザに「HTTP/3も使えるよ」と伝える
add_header Alt-Svc 'h3=":443"; ma=86400';
server_name example.com;
root /var/www/html;
}
ポイントは2つあります。1つはAlt-Svcヘッダーです。ブラウザはまずTCP(HTTP/2)で接続し、このヘッダーを見て「次回からUDP 443でHTTP/3を試す」と学習します。ヘッダーがなければHTTP/3は永遠に使われません。もう1つはreuseportで、複数のワーカープロセスでUDPソケットを共有するために必要です。設定後はnginx -tで文法確認をしてからリロードします。
nginx -Vの出力(標準エラー出力に出ます)にwith-http_v3_moduleが含まれるか確認し、HTTP/3対応ビルドであることを確かめる- 上記のようにserverブロックへ
quicとhttp3 on;、Alt-Svcを追加する - ファイアウォール(ufw/firewalld)とクラウドのセキュリティグループで「UDP 443」を許可する
nginx -tで文法確認をしてからsystemctl reload nginxで反映する- 次項の方法でブラウザとcurlから確認する
curlで確認する
curlは、ビルド時にQUICライブラリ(ngtcp2など)が組み込まれている場合だけ--http3を使えます。まずcurl --versionの「Features」行にHTTP3が含まれているか確認してください。macOSのHomebrew版curlやDockerイメージのcurlimages/curlは対応版が入手しやすい選択肢です。
# HTTP/3対応ビルドか確認
curl --version | grep -i http3
# HTTP/2で接続し、使われたバージョンを表示
curl -sI --http2 https://example.com/ -o /dev/null -w 'http_version=%{http_version}\n'
# HTTP/3を強制して接続(失敗すればHTTP/3が使えない)
curl -sI --http3-only https://example.com/ -o /dev/null -w 'http_version=%{http_version}\n'
# Alt-Svcヘッダーが返っているか
curl -sI https://example.com/ | grep -i alt-svc
--http3はHTTP/3を試しつつ失敗時にTCPへフォールバックするため、確認用途では--http3-onlyのほうが結果が明確です。http_version=3と出れば成功です。
ブラウザの開発者ツールで確認する
Chromeなら開発者ツールのNetworkタブで、列見出しを右クリックして「Protocol」を表示します。h2ならHTTP/2、h3ならHTTP/3です。注意点として、初回アクセスはAlt-Svcを知らないので必ずh2になります。リロードして2回目以降にh3に切り替わるのが正常な挙動です。Firefoxでも同様にProtocol列(HTTP/3の場合は「HTTP/3」)で確認できます。
トラブル事例:有効化したのにh3にならない
症状
Nginxにlisten 443 quicとhttp3 on;を追加し、nginx -tも通ってリロード済み。ところがChromeで何度リロードしてもProtocol列はh2のまま。curl --http3-onlyを打つと「Failed to connect」で終わる、という状況です。
原因
このケースで圧倒的に多いのは、UDP 443が経路上のどこかで閉じていることです。TCP 443は当然開けていますが、クラウドのセキュリティグループやufwに「UDP 443」を別途許可していないと、QUICのパケットはサーバーに届く前に破棄されます。ブラウザは数秒以内にHTTP/3の試行をあきらめてh2へ戻るので、エラーが目に見えず「効いていないだけ」に見えるのが厄介な点です。次点の原因はAlt-Svcヘッダーの付け忘れ、あるいはadd_headerをlocationブロック内にも書いていて上位の設定が打ち消されているパターンです(Nginxのadd_headerは下位ブロックで1つでも定義すると上位のものを継承しません)。
対処
サーバー側でss -ulnp | grep 443を実行し、NginxがUDP 443を待ち受けているかをまず確認します。待ち受けていればサーバー内の設定は正しく、あとは経路の問題です。ufwならufw allow 443/udp、AWSならセキュリティグループのインバウンドに「カスタムUDP 443」を追加します。それでもだめならcurl -sI https://example.com/ | grep -i alt-svcでヘッダーの有無を確認し、locationブロック内のadd_headerの有無を疑ってください。ファイアウォールの操作に不安があればLinuxファイアウォール設定入門を参照してください。
実務での判断:HTTP/3にどこまで投資するか
効果が出やすい条件と出にくい条件
HTTP/3の効果が大きいのは、モバイルユーザーが多いサイト、海外など遅延の大きい地域からのアクセスが多いサイト、小さなリソースを大量に読み込むSPAです。逆に、社内LANからしか使わない業務システムや、1リクエストで大きなデータを返すAPIでは体感差はほとんどありません。HTTP/2に上げるだけでHTTP/1.1からの改善は十分に大きいので、まずHTTP/2を確実に有効化し、その上でCDNのHTTP/3トグルをオンにする、という順序が費用対効果の高い進め方です。
また、HTTP/3を有効にしてもTCP側は必ず残す必要があります。企業ネットワークやホテルのWi-FiなどUDPを制限している環境は実在するため、HTTP/3は「使えるときに使う追加経路」と位置づけ、HTTP/2を土台として整えておくのが正しい設計です。バックエンド間の通信でHTTP/2を活用したい場合はgRPC入門も参考になります。
まとめ
HTTP/1.1のHoLブロッキングをHTTP/2は多重化で解決しましたが、TCPという土台に起因する詰まりは残りました。HTTP/3はQUICというUDP上の新しいトランスポートで、ストリーム独立の再送、1-RTT/0-RTTの接続確立、コネクションマイグレーションを実現し、モバイルや高遅延環境で特に効果を発揮します。実務では、まずHTTP/2を確実に有効化し、CDNやNginxでHTTP/3を追加、UDP 443の開放とAlt-Svcヘッダーを忘れず、curlと開発者ツールで実際にh3になっているかを確認する、という流れで進めてください。
プロトコルの選定やNginx・CDNの構成設計に手が回らない場合は、Harmonic Societyのシステム開発・インフラ支援にお気軽にご相談ください。
Harmonic Society
この記事の内容、自社の業務でも活かせそうですか?
ローカルLLM・AI・クラウドなどの技術導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK。まずは30分の無料相談から。売り込みはしません。
関連記事
Related / 9 articles
Notes & Insights
- プログラミング
DDoS攻撃の仕組みと対策入門|レイヤー別の防御とCDN・クラウドの活用
DDoS攻撃をボリューム型・プロトコル型・アプリ層に分けて仕組みを解説し、自前サーバーで防げない理由、CloudflareやAWS Shieldの標準防御、オリジンIPの隠し方、レートリミットとBot対策、攻撃を受けたときの初動、費用が跳ね上がるDenial of Walletへの備えまでわかります。
- プログラミング
WAFとは?仕組み・導入パターン・誤検知対策|Webアプリを攻撃から守る実践ガイド
WAFがファイアウォールやIDSと何が違うのか、シグネチャとマネージドルールの仕組み、Cloudflare WAF・AWS WAF・ModSecurityの比較、フォーム送信がブロックされる誤検知の調査と例外設定、ログ監視、WAFが代替できないことまで実践的に解説します。
- プログラミング
セキュリティヘッダー入門|CSP・HSTS・X-Frame-Optionsの設定と効果を実践解説
CSP・HSTS・X-Frame-Options・X-Content-Type-Optionsなど主要セキュリティヘッダーが防ぐ攻撃と、CSPのReport-Onlyからの段階導入、nonce/hash、HSTS preloadの不可逆リスク、Nginx・Next.js・Astroでの設定例、確認方法を解説します。
- プログラミング
クラウドの通信費(Egress)入門|データ転送量課金の仕組みと転送コストを抑える設計
クラウドの「受信無料・送信有料」の原則、AZ間・リージョン間・インターネット向けの単価差、NATゲートウェイ処理料の罠、CDNで転送量を減らす方法、バックアップやログ転送の見落とし、請求書で転送料を特定する手順を解説。想定外の請求を防げます。
- プログラミング
秘密情報をGitに入れない仕組み|.gitignore・git-secrets・履歴から漏れた鍵の削除
APIキーや.envをGitにコミットしてしまう典型経路と、.gitignore・.env.exampleの運用、pre-commitでのgitleaks検知、GitHub secret scanningの活用、漏れた鍵の無効化と履歴書き換え(git filter-repo)の手順を解説。仕組みで再発を防げます。
- プログラミング
開発・ステージング・本番環境の分離設計|環境差分をなくす構成とアクセス制御
開発・ステージング・本番それぞれの目的と、構成をコードで揃える方法、環境別の設定注入、本番データを使わないテストデータ戦略、ステージングの保護(Basic認証・IP制限・noindex)、コストを抑える運用までを解説。環境差分による本番障害を防げます。
- プログラミング
ngrok・Cloudflare Tunnelでローカルを公開|Webhook開発とデモ環境の作り方
NAT内のローカル環境にStripeやLINEのWebhookを届けるトンネリングの仕組みを解説。ngrok・Cloudflare Tunnel・localtunnelの比較、固定ドメインと認証、リクエスト検査、公開時のセキュリティ、自宅サーバー公開への応用までわかります。
- プログラミング
ローカル開発環境のHTTPS化|mkcert・hostsファイル・自己署名証明書の正しい使い方
ローカル開発をHTTPS前提にすべき理由(Secure Cookie・Service Worker・OAuth)と、mkcertでローカルCAを作りhostsで独自ドメインを割り当ててVite・Next.js・Dockerで使う手順を解説。証明書警告を無視する癖の危険も理解できます。
- プログラミング
localhost・0.0.0.0・127.0.0.1の違い|ポートとUnixソケットを理解して「つながらない」を解決
localhost・127.0.0.1・0.0.0.0の意味の違い、Dockerで外から接続できない原因、host.docker.internal、ポート競合の調べ方、Unixソケットの利点と権限、1024未満ポートの制約を解説。「つながらない」を仕組みから解決できます。
Harmonic Society
「読んで終わり」にせず、自社の業務で試してみませんか?
AI・ローカルLLM・クラウドの導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK・売り込みなし。
無料・30分・オンラインOK|1営業日以内に返信します