HTTP/3|Webエンジニアが知るべき新常識 ─ QUICやコネクションマイグレーションなどを学ぶ
新しい通信プロトコルとして普及が進んでいるHTTP/3については、エンジニアHubでも過去に概論的な記事を掲載しています。今回はアプリケーション開発者が自社サービスでHTTP/3を採用することを想定して、仕様上の留意点や、どのように使い始めるか、そしてサイトを制作する際に注意しておきたいポイントまでを藤吾郎(gfx)さんに解説していただきました。
本記事ではHTTP/3およびその通信プロトコルであるQUICを、アプリケーション開発者として活用する立場で入門します。HTTP/3は、HTTP/1.1とHTTP/2に続く新しいメジャーバージョンのHTTPプロトコルです。HTTP/3はHTTP/1.1およびHTTP/2を置き換えるポテンシャルを持っています。将来的にほとんどのインターネットトラフィックはHTTP/3を使うようになっていくと筆者は考えています。
HTTP/3は、通信プロトコルとしてTCPではなくUDPベースであり、TCP同様の信頼性がある通信を行うためにQUICプロトコルを採用しています。HTTP/3はHTTP/1.1やHTTP/2と比較してさまざまな利点がある一方で、QUICプロトコルともどもバイナリプロトコルであること、仕様も実装も複雑で難解であることから、導入をためらっているケースも多いでしょう。
そこで本記事では、HTTP/3そのものと、その基盤となるトランスポートプロトコルのQUIC(IETF QUIC)について、主にウェブアプリケーションの開発者向けに、運用面で知っておくとよいことを中心に解説します。なお、本記事は筆者の個人的な見解であり、何らかの組織を代表とするものではありません。
※ 本記事はChatGPT / GPT-4を執筆の補助に使いました。内容については一字一句すべて筆者に責任がありますが、ChatGPTのおかげで大幅に執筆時間を短縮し、また品質も向上できたと思います。
HTTP/3とQUICにはどういった特徴があるのか?
HTTP/3は、HTTP/1.1とHTTP/2に続く新しいメジャーバージョンのHTTPプロトコルです。HTTP/1.1とHTTP/2では、テキストプロトコルとバイナリプロトコルという差はあるものの、いずれもTCPで通信することが前提となっていました*1。
HTTP/3の基盤となるトランスポートプロトコルはQUICです。QUICは、UDPをベースにしてTCPのように信頼性のある通信をするためのプロトコルです。さらにTCPよりも多重化された通信に適していて、TCPよりも拡張しやすい通信プロトコルとして設計されています。
オリジナルのQUIC(Google QUIC)はGoogleが開発したもので、その後IETF(Internet Engineering Task Force)によって標準化されたため、現在のQUICはIETF QUICとも呼ばれます。なお、オリジナルのGoogle QUICとIETF QUICは互換性がない別物となっています。
HTTP/3は当初「HTTP/2 over QUIC」と呼ばれていました。これは、HTTP/2の特徴を継承しているからです。具体的には、バイナリプロトコルであること、通信が多重化されていること、HTTPヘッダを圧縮できることが挙げられます。
複数ストリームなどによるパフォーマンスの向上
HTTP/3は、HTTP/1.1やHTTP/2よりもパフォーマンスが向上する状況が多くなっています。これは、HTTP/3が複数の新しい技術と改善点を組み込んでいるからです。
まず、HTTP/1.1と比較した場合のパフォーマンスの向上は、HTTP/2と同様の理由によります。つまり、HTTP/3は1つの接続上で複数のストリームを同時に管理できます。これにより、HTTPにおけるHoL(Head-of-Line) Blockingが発生する可能性が減少します。HoL Blockingとは、1つの時間のかかるリクエストが、後続のリクエストをブロックしてしまう現象です。
HTTP/1.1では、ブラウザは少数のTCP接続でリクエストとレスポンスの送受信を行います。このとき、いずれかのTCP接続に遅いHTTPリクエストの送受信があると、その遅いリクエストのが終わるまでは、他のリクエストを始められません。しかし、HTTP/2およびHTTP/3は、1つの接続で複数のリクエストを同時に送受信できます。そのときに時間のかかるリクエストがあったとしても、他のリクエストの送受信は継続できます。その結果、全体のレスポンス時間が短縮され、体感速度が向上します。
さらに、HTTP/3には接続の安定性を高める機能として、コネクションマイグレーションがあります。これによりクライアントのIPアドレスが変わった場合でも、既存の通信を継続することが可能となります。この機能は特にモバイルデバイスなど、接続環境が変化する可能性のある状況下での通信の安定性を向上させる可能性があります。
TLS暗号化によるセキュリティ
HTTP/3とその基盤となるQUICは、初めからセキュリティに重きを置いた設計がされています。QUICはTLS 1.3ベースの暗号化を常に行います。暗号化のないQUICは存在しません。HTTP/3はトランスポートプロトコルとして必ずQUICを使うため、HTTP/3に暗号化をしない形式はありません。
TLSの運用面に関しては、HTTP/1.1やHTTP/2と変わりません。TLS証明書も共有できます。
コネクションマイグレーションによる通信の安定化
QUICによる通信を安定化する機能として、コネクションマイグレーションがあります。これによりクライアントとサーバ間の通信経路が変わった場合でも、接続を継続することが可能となります。これは、QUICがIPアドレスとポート番号ではなく、クライアントとサーバ間で取り決めた識別子(CID)によって通信相手を特定するからです。
コネクションマイグレーションが効果を発揮するのは、スマートフォンなどのモバイルデバイスです。モバイルデバイスはWi-Fiとモバイルネットワークの切り替えを行うことがよくあります。このように接続環境が頻繁に変化する可能性のある状況下も、コネクションマイグレーションにより安定した通信を行える可能性が高まります。実際には通信がまったくできないとタイムアウトによって接続が切れるので、「接続が切れにくくなる」という程度ですが、それでもパフォーマンス向上効果は高いはずです。
HTTP/3とQUICの仕様における詳細と注意点
HTTP/3とQUICを利用する上で理解しておいた方がよい仕様上の注意点をいくつか紹介します。
QUICによるトランスポートレベルでのHoL Blockingの解消
HTTP/3は、HTTP/2と比較してもパフォーマンスが向上する場面があります。これは、HTTP/3がTCPではなく、新しいトランスポートプロトコルであるQUICを採用しているからです。
HTTP/2は、HTTPにおけるHoL Blockingは解消できます。しかしTCPを使う以上、TCPのHoL Blockingの影響は受けてしまいます。TCPのHoL Blockingとは、一部のパケットが遅延または失われた場合に、それが後続のパケットの処理をブロックしてしまう現象です。HTTP/2では、HTTP/1.1よりもTCP接続の数が少ないため、TCPのHoL Blockingの影響を強く受けやすいのです。そのため、接続が不安定でパケットが失われやすいときには、HTTP/1.1よりもパフォーマンスが低下することがあります。
一方、QUICでは、トランスポートプロトコルにおけるHoL Blockingが発生する可能性が減少します。QUICは通信中に一部のパケットが失われても、それが後続のパケットの処理に影響を与えないように設計されているからです。この点がHTTP/3の大きな特徴であり、特にネットワークの状態が不安定でパケットが失われやすい状況において、HTTP/1.1やHTTP/2よりも高速に通信できることが多いのです。
0-RTTによるセッション再開時のパフォーマンス向上とセキュリティ上の注意点
HTTP/3では、同じ接続先と2回目以降に接続するとき、0-RTT(early-dataとも)という機能を利用できます*2。これは、セッションの再開時に、ハンドシェイクとHTTPリクエストを同時に送信することができるというもので、通信のパフォーマンスの向上に寄与します。
ただし、この0-RTTはオプトインの機能です。この機能をデフォルトで有効にすると、リプレイ攻撃の危険性があるためです。リプレイ攻撃では、非攻撃者が行う正しいHTTPリクエストが攻撃者によってコピーされ、再送されます。リプレイ攻撃が成功すると、データの更新を伴うHTTPリクエスト(例えば決済APIの呼び出し)で、意図しないデータの更新が行われる可能性があります。一方で、データの更新を伴わない冪等なHTTPリクエストであれば、リプレイ攻撃が成功しても大きな問題はありません。
0-RTTは接続ごとに可否を決める設定であり、エンドポイントやHTTPメソッドごとにオン・オフはできませんが、アプリケーションレベルで制御は可能です。実際に各CDNでは、0-RTTの安全な運用のため、0-RTTを有効にしていても特定の条件以外では拒否することがあります。この「0-RTTの拒否」は、HTTPステータスコード「425 Too Early」によって行われ、HTTPクライアントは425をみるとリクエストを自動的に再送します。そのためリクエストは0-RTTではなくなりますが、エンドユーザはそのことに気が付きません。
例えばFastlyは、GETまたはHEAD以外のHTTPメソッドのとき0-RTTを拒否します(参照リンク)。Cloudflareでは「クエリストリングのないGET」以外での0-RTTを拒否します(参照リンク)。0-RTTのデフォルトの扱いや具体的な運用はCDNによって異なるため、利用するCDNのドキュメントを必ず確認してください。
オリジンサーバでも、GETでデータの更新を行なうエンドポイントがある場合は、0-RTTの対応が必要です。CDNは、0-RTTリクエストを受けると、early-data: 1
をオリジンサーバへのリクエストヘッダに付加します。これにより、オリジンサーバは0-RTTリクエストを特定でき、冪等でないエンドポイントで425を返すことができます。CDNのデフォルトの振る舞いが十分でない場合は、このように対応すれば0-RTTを安全に運用できます。
以上のように、TLS 1.3による0-RTTはパフォーマンス向上のために有用ですが、リプレイ攻撃のリスクが伴うため、正確な理解と対策が必要です。
Alt-SvcによるHTTP/3の利用可能性の通知
ウェブブラウザは、ウェブサーバがHTTP/3で利用可能であると宣伝(advertise)してくるまで、HTTP/3での通信を試みません。ブラウザは未知のウェブサイトにHTTPリクエストを送るとき、まずTCPのHTTPプロトコル(HTTP/1.1またはHTTP/2)を試みます。リクエストを受け取ったサーバはそのレスポンスヘッダに「HTTP/3で通信可能である」という情報を含めて返します。この代替サービスの宣伝は、Alt-Svcレスポンスヘッダによって行われます。
HTTP/2の場合は、TCP接続後かつHTTPリクエスト送信前の段階で、クライアントとサーバ間でお互いに利用可能なHTTPプロトコルを確認できます。TLSハンドシェイクがそのタイミングで行われるため、そのメタデータに使用可能なHTTPプロトコルの一覧を含めれば、「最初のHTTPリクエスト」のプロトコル選択に間に合うわけです。
しかし、UDPを使うHTTP/3では事情が異なります。もしサーバがHTTP/3をサポートしているかどうか不明であるなら、まずはTCPによるHTTP通信を試みる方が接続できる可能性が高くなります。そのためAlt-SvcによってHTTP/3のサポート状況が確認できるまでは、TCPによる通信を優先すべきなのです。
これに対して、例えば「最初にHTTP/3で通信を試みて、ダメならあらためてTCPのHTTP通信のリクエストを送る」という方法は都合がよくありません。普及の初期段階ではHTTP/3に対応するサーバが少ないため、この方法ではHTTP/3がサポートされていない場合に接続の試みが無駄になり、かえってパフォーマンスを損ないます。「TCPとUDP両方を同時に試みる」という方法もありますが、これはパフォーマンスは損なわないものの、サーバがTCPとHTTP/3双方をサポートしているときに、クライアントとサーバの両方が余計なリソースを消費することになります。
なお、ブラウザがリクエストを最初からHTTP/3にできないという問題については、DNSがHTTP/3を宣伝することで解決する機能が検討されています。DNSによるHTTP/3の宣伝は、2023年現在、まだ簡単に利用できる状態ではないようですが、HTTP/3のメリットを有効活用するためには大きな意味を持つでしょう。
また、モバイルアプリのAPIクライアントのように、接続先のサーバが既知のものに限定されている場合は、Alt-SvcによるHTTP/3の宣伝を待たずに、最初の接続からHTTP/3を利用できます。HTTP/3の通信にとって宣伝そのものは必須ではないからです。
【コラム】新しい技術を評価するためのメンタルモデル ─ 仕様と実装の違い
新しい技術、例えばHTTP/3のようなものを評価する際には、「仕様の品質」と「実装の品質」を分けて考えることが重要です。これらはある程度独立した要素であり、それぞれが技術全体の品質とパフォーマンスに影響を与えます。
一般に技術の評価や比較の過程で、仕様と実装の違いが混同されることがあります。特に、技術間のパフォーマンスを比較するベンチマークは、仕様の性能の比較ではなく、実装の品質の比較になりがちです。
このような観点では、HTTP/3も例外ではありません。HTTP/3の性能評価の結果が満足なものではなかったとしても、それは実装の品質がまだ十分でなかったからかもしれません。例えばコネクションマイグレーションはまだ実装されていないことも多いのですが、いずれ普及すればUXの向上に大きく寄与するはずです。
もちろん実際に新しい技術を本番環境に採用する際には、観察できるパフォーマンスこそが重要です。つまり仕様よりも実装の品質を見ることになります。仕様がどれほど優れていても、具現化する実装の品質が不十分であれば、その技術を採用することで製品の品質が低下する可能性があります。それは避けるべきことです。
したがって、現時点でHTTP/3の特定の実装を評価し、導入の利点が十分でないとして採用しないと決定する可能性はあります。しかし、状況が変わればその評価も変わる可能性があります。数年後、実装がさらに洗練され、品質が向上したとすれば、その時点で再評価することが必要になるでしょう。
そういうわけで、新しい技術を評価する際には、仕様と実装の違いを理解し、それぞれを独立して評価するマインドセットが必要だと言えます。これにより技術の真の価値を適切に評価し、その時々で最善の選択をできるでしょう。
HTTP/3でサービスを運用するには?
HTTP/3とQUICを利用する最も簡単で費用対効果のよい方法は、CDNの「HTTP/3」もしくは「QUIC」の機能を有効にすることです。この「HTTP/3の有効化」には、レスポンスにAlt-Svcレスポンスヘッダを加えることも含みます。
CDNをすでに使っている場合、静的ファイルのほとんどがCDNから配信されると考えられます。HTTP/3を有効にする具体的な操作についてはCDN次第ですが、多くの場合、所定の設定フラグを有効にするだけです。CDNによる管理という意味では「マネージドHTTP/3サーバ」と言えるかもしれません。
CDNでHTTP/3を使用する設定を有効にすると、CDNとブラウザ間の通信はHTTP/3になります。一方で、CDNとオリジンサーバ間の通信は、HTTP/1.1またはHTTP/2のままです。現在のところHTTP/3の普及は初期段階ですから、まずこの最小限のアクションでHTTP/3の恩恵を得られるようにすることをおすすめします。
この構成をまとめると次の図のようになります。そしてこの構成は、この先数年間のデファクトスタンダードになるでしょう。
なお、CDNとオリジンサーバ間の通信をHTTP/3にすることは、現時点ではそれほど重要ではありません。QUICは不安定な通信経路でこそ性能を最大限に発揮するため、CDNとオリジンサーバ間のような安定した通信経路では、その効果が相対的に小さいからです。
HTTP/3のサポート状況とフォールバック戦略
HTTP/3のサポート状況に関しては、2023年現在、すべてのモダンブラウザ(Chrome・Firefox・Edge・Safari)で実装済みで、また多くのブラウザでデフォルトで有効になっています。ただしSafariに関しては、HTTP/3は実装されているものの、デフォルトではほとんどのユーザーで無効になっているようです。
HTTP/3のサポート状況はcaniuse.comで確認できます。
▶ HTTP/3 protocol | Can I use... Support tables for HTML5, CSS3, etc
HTTP/3が実際に使われるかどうかを確認するには、HTTP/3で配信しているウェブサイトにアクセスするのが簡単です。例えばhttp3.isにアクセスすると、HTTP/3でアクセスしたときとそれ以外で表示される内容が変化します。
ただし、すでに述べたようにHTTP/3が有効になっているブラウザでも、最初のアクセスではHTTP/3は使われません。
リロードするとHTTP/3が使われることを確認できるはずです。
特定のウェブサイトがHTTP/3をサポートしているかどうかは、ブラウザの開発者ツールで「ネットワーク」タブから確認できます(Chrome・Edge・Firefox・Safariの場合)。ただし、「プロトコル」カラムはデフォルトで表示されていないことも多いので、カラム名を右クリックして「プロトコル」を表示する必要があります。
なお、ブラウザ以外のHTTP/3クライアントは、まだ十分に普及しているとは言えないようです。特にスマートフォンにおいては通信経路の切り替えを含めて通信が不安定なことが多いため、HTTP/3の恩恵が大きいプラットフォームですが、現在のところスマートフォンのアプリケーションで利用可能なのは、iOS標準のHTTPクライアントくらいです(参照リンク)。
今はまだ、スマートフォンアプリケーションの通信でHTTP/3を前提とするには時期尚早かもしれません。
HTTP/3を前提としたウェブアプリケーションの設計
HTTP/3は、HTTP/2の利点を継承しています。そのためHTTP/3時代においても、HTTP/2時代同様の設計が効果的です。またHTTP/3は、たとえサーバが対応していても、通信時に常にHTTP/3が使われるわけではありません。その意味でも「HTTP/3向けの設計」は「HTTP/2およびHTTP/3向けの設計」であるべきなのです。
HTTP/3がウェブアプリケーションの設計に与える影響としては、「小さなファイルを大量にダウンロードする」というタスクが得意だという点があげられます。その理由は、前述のとおりHoL Blockingが起きないようになっていること、そしてヘッダ圧縮が行われるため、HTTPヘッダのサイズがパフォーマンスに影響を与えにくいことです。
一方でHTTP/1.1時代は、小さなファイルを大量にダウンロードするより、少数の大きなファイルにまとめてダウンロードさせる方がパフォーマンスが良かったため、そのための技術が発展しました。
例えば、CSS spritesは複数の画像を1つの大きな画像にまとめてダウンロードし、それをCSSで切り出して利用する技術です。これはまさにHTTP/1.1においてパフォーマンスを改善するために開発されました。しかし、HTTP/3では複数の小さなリクエストを効率よく処理できるため、不必要なデータをダウンロードせずに、必要なデータだけをダウンロードする方が最終的にダウンロードする量が減り、帯域幅を有効に利用できます。このため、CSS Spritesの使用は、HTTP/3の環境ではパフォーマンスを悪化させる可能性があります*3。
JavaScriptやCSSファイルをアプリケーションビルド時に少数の大きなファイルへと結合(バンドル)するのもよく使われる技術です。こちらは、結合時にファイルサイズそのものを減らすminifyやtree-shakingなどの要素技術もあるため、HTTP/3時代にまったく不要になったとは言えません。
しかしHTTP/3時代には、ファイルの依存関係をそのファイルのままで維持して、必要最小限のファイルをダウンロードできるようになっていくのではないでしょうか。こういったバンドラ周りはまだ発展途上の技術ですが、HTTP/3の普及とともに、より効率的なバンドラの開発が進むことでしょう。
まとめ ─ 機会があればHTTP/3を有効にしてみよう
本記事では、ウェブエンジニアが知っておくべきHTTP/3の仕様と使い方をまとめました。HTTP/3は普及段階の技術のため、全面的に採用するのはまだ難しいかもしれません。しかし、現状でもCDNを介するのであれば比較的簡単に利用できます。
また、HTTP/3とQUICは標準化されて終わりではありません。特にQUICは、TCPよりも試行錯誤のサイクルを高速に回せるため、この先でもさまざまな改善が行われていく見込みです。
この記事を読んで「とりあえず利用中でCDNでHTTP/3を有効にしてみよう」と思ってもらえれば幸いです。
参考リンク集
本稿をさらに理解するうえで参考になるリンクをいくつか挙げておきます。
- HTTP/2、覚えていますか?
- curlの作者として知られるDaniel Stenberg氏による「HTTP/3 explained」より
- HTTP/3入門, 2021
- 後藤ゆき氏が「WEB+DB PRESS Vol.123」に執筆した記事の再掲載
- HTTP/3とQUICはなぜ必要になり、どのように標準化されてきたのか?, 2021
- 同じく後藤氏がエンジニアHubに執筆した記事
- HTTP/3はどうやってWebを加速するか?(前編) / 同(後編), 2020/2021
- Fastlyの奥一穂氏による講演をPublickeyがテキスト化した記事
- 同じく奥氏がツイートしたQ&Aのスレッドも参照
- QUICをゆっくり解説, 2021-2022
- IIJのエンジニアブログに山本和彦氏が執筆した19回におよぶ長期連載
- RFCs
藤 吾郎(ふじ・ごろう) gfx / gfx.bsky.social
編集・制作:はてな編集部
*1:HTTP/1.1とHTTP/2のトランスポートプロトコルがTCPでなければならない、というわけではありません。実際にはTCPと同等の信頼性のあるトランスポートプロトコルであればよく、例えばUNIX domain socketなどもよく使われます。
*2:実際には、0-RTTはHTTP/3の専用機能ではありません。HTTP/1.1とHTTP/2でもTLS 1.3を使うときは0-RTTが使えます。0-RTTの運用における注意点もHTTPのバージョンにかかわらず同じです。
*3:CSS spritesに関してはMDNで "Note: When using HTTP/2, it may in fact be more bandwidth-friendly to use multiple small requests." (訳: HTTP/2を使うときは、複数の小さなリクエストを行う方が(CSS Spritesよりも)帯域に優しいと思われます)という記述もあります。これはHTTP/3にも当てはまります。 https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Images/Implementing_image_sprites_in_CSS