Development
15 min read
34 views

マルチプレックス型マルチテナントトンネル:HTTP/3 Extended CONNECTで50以上のローカルサービスを単一のQUIC接続上で実行

HTTP/3 Extended CONNECTを使用したマルチプレックス型マルチテナントトンネルで、1つのTCPポートに50以上のローカルサービスを並行して実行する方法を解説します。

IT
InstaTunnel Team
Published by the InstaTunnel team | Editorial policy
マルチプレックス型マルチテナントトンネル:HTTP/3 Extended CONNECTで50以上のローカルサービスを単一のQUIC接続上で実行

Quick answer

マルチプレックス型マルチテナントトンネル:1つで50以上のサービスを実行: webhook testing answer

For local webhook testing, run your app locally, expose it with a public HTTPS tunnel, and paste the stable callback URL into the provider dashboard.

How do I test webhooks on localhost?

Start your local server, open a public HTTPS tunnel to that port, configure the provider webhook URL, and inspect events in your local logs.

Why does a stable webhook URL matter?

Stable URLs prevent provider dashboards from needing manual callback updates every time you restart a tunnel.

ローカルサービスをインターネットに公開するのは簡単です。しかし、信頼性を持って多数のサービスを公開するのは難しいです。開発者のマシンやエッジゲートウェイでは、REST API、gRPCエンドポイント、WebSocketサーバー、管理UIなどを同時に動かしていることがあります。それぞれに独自のポートやTCP接続、トンネルプロセスを割り当てるとコストがかさみます。

この記事では、現代のトンネルがこの問題をどう解決しているかを紹介します。複数のサービスを1つのアウトバウンド接続でマルチプレックスし、その接続はますますUDPポート443上のQUICになっています。また、HTTP/3のExtended CONNECTが何を可能にし、何をできないのかについても解説します。詳細はトンネル設計にとって重要です。


ボトルネック:サービスごとに1ポートまたは1接続

トンネルの規模が拡大すると、次の3つの問題が浮上します。

1. 各接続のオーバーヘッド

各サービスにTCP + TLS接続を割り当てると、以下のコストが発生します:

  • ハンドシェイクコスト。 TCPハンドシェイクとTLSハンドシェイクを繰り返す必要があります。
  • カーネルの状態とファイルディスクリプタ。 各接続はソケットやバッファ、TCP状態マシンを保持します。これは小規模なゲートウェイやノートPCで特に重要です。
  • NATやファイアウォールの状態管理。 追加のフローごとに状態テーブルにエントリが増え、NATのマッピングは一定時間後にタイムアウトします。

2. ポートのスプロール

従来のSSHフォワーディング(-L / -R)では、各フォワーディングサービスごとに異なるリッスンポートが必要です。これにより、ファイアウォールルールやポート管理が複雑になり、制限の多いネットワークではブロックされやすくなります。

3. TCPヘッド・オブ・ライン(HoL)ブロッキング

HTTP/2のように、多数の論理ストリームを1つのTCP接続上でマルチプレックスすると、接続数は減りますが、新たな問題も生じます。TCPは順序付きのバイトストリームを配信するため、パケットの損失があると、そのストリームだけが遅延し、後続のストリームもブロックされてしまいます。大容量のアップロードや遅延に敏感なAPI呼び出しが同じトンネルを共有していると、遅延が発生します。

既存のトンネルの対応状況

多くの成熟したトンネル製品は、複数のサービスを1つの接続上でマルチプレックスし、各サービスごとに新たな接続を開くのではなく、既存の接続を使い回しています:

  • ngrokはmuxadoというストリームマルチプレックスプロトコルを使用しており、そのオープンソースのRustやGo SDKはこれをngrokのトンネルを支えるプロトコルとしています。
  • Cloudflare Tunnel (cloudflared)は、ポート7844上で少なくとも2つの異なるCloudflareデータセンターに4つのアウトバウンド接続を開きます。デフォルトの--protocol autoモードはUDP上のQUICを優先し、UDP接続が確立できない場合はHTTP/2にフォールバックします。レガシーのh2muxプロトコルはサポートされていません。cloudflaredのQUICトランスポートはquic-goライブラリ上に構築されています。

この「1つの接続に多くのサービスを乗せる」というアイデアは、すでに実践的な手法です。次に重要なのは、どのマルチプレックス層を選び、その仕組みをどう構築するかです。


なぜQUICが状況を変えるのか

QUIC(RFC 9000)はUDP上で動作し、TLS 1.3(RFC 9001)と統合されています。ネイティブのストリームを持ち、損失回復はストリームごとに追跡されます。データのパケットが失われても、そのストリームだけが再送待ちになり、他のストリームは引き続きデータを受け取れます。

ただし、以下の2つの注意点があります:

  • 順序はストリーム内で保持される。 各ストリームは独自の順序付きバイトストリームであり、損失があってもそのストリームだけが遅延します。
  • HTTP/3のヘッダー圧縮は一部ブロッキングを再導入する可能性がある。 QPACK(RFC 9204)はHPACKと比べてHoLブロッキングを軽減しますが、動的テーブルの参照はデコーダがテーブル更新を受信していないとブロックされることがあります。SETTINGS_QPACK_BLOCKED_STREAMS設定でこれを制御します。

QUICはトンネルに次のような追加のメリットももたらします:

特徴 トンネルにとって重要な理由
UDPソケット1つ、接続1つ NATやファイアウォールの追跡に必要な4つ組と、UDP 443の単一ルール
ストリームごとの損失回復 大容量の転送が遅延しても、他のサービスには影響しない
1RTTハンドシェイク、オプションの0RTT再開 再接続を高速化。ただし0RTTは早期データのリプレイに注意
接続IDとマイグレーション ネットワーク変更(Wi-Fiからモバイルへ)後も接続を維持可能(RFC 9000)
DPLPMTUD内蔵 パスの状態に応じてパケットサイズを拡大可能

接続IDとマイグレーション

QUICの接続はConnection IDsによって識別され、IP/ポートの4つ組だけではありません。Connection IDは可変長で、RFC 9000では最大20バイトです。固定の64ビット値ではありません。

ネットワークパスが変わった場合、クライアント側は新しいパスをプローブし、マイグレーションします。まずパス検証を行うため、即時ではなく条件付きです。アプリケーションやミドルボックスも干渉します。


HTTP/3 Extended CONNECT:何ができるのか

プレーンなHTTPのCONNECTはプロキシを通じてTCPトンネルを開きます。Extended CONNECTはRFC 8441でHTTP/2用に定義され、:protocol疑似ヘッダーを追加し、単一のCONNECTストリームで生のTCP以外のものも運べるようにします。RFC 9220はこれをHTTP/3に拡張し、SETTINGS_ENABLE_CONNECT_PROTOCOL設定(値0x08)を登録しています。タイトルは「WebSocketのHTTP/3によるブートストラップ」ですが、仕組みは一般的です。ほかの仕様もこれを基にしています:

  • WebSocket over HTTP/3(:protocol = websocket)、最も基本的な用途。
  • CONNECT-UDP(RFC 9298、「HTTPでUDPをプロキシ」)、UDPデータグラムをトンネル化。
  • CONNECT-IP(RFC 9484、「HTTPでIPをプロキシ」)、IPパケットをトンネル化。
  • WebTransport over HTTP/3(現行のIETFドラフトdraft-ietf-webtrans-http3、2026年7月のリビジョン16)、各セッションはExtended CONNECTリクエストから開始。

Extended CONNECTリクエストは以下の疑似ヘッダーを持ちます:

疑似ヘッダー 例 備考
:method CONNECT
:protocol websocket、connect-udp、webtransport、またはプライベートトークン(例:tunnel-v1) ストリーム上の通信プロトコルを指定。tunnel-v1はプライベート設計の例です。
:scheme https Extended CONNECTには必須
:authority service-50.tunnels.example.com ルーティング用のターゲットホスト
:path / 必須

両側はHTTP/3のSETTINGSフレームでサポートを通知します。クライアントはサーバーの設定を受信してからしかExtended CONNECTリクエストを送れません。例としてquic-goはこれを確認します。


方向性の問題:誰がストリームを開くのか

最も重要な設計ポイントであり、誤りやすいのは誰がストリームを開くかです。

HTTP/3では、リクエストストリームはクライアント起動の双方向QUICストリームです。最初のリクエストはストリーム0、その後4、8、12と続きます。RFC 9114は、HTTP/3はサーバー起動の双方向ストリームを使わないと規定しており、クライアントがそれを受信した場合はH3_STREAM_CREATION_ERRORのコネクションエラーとして扱う必要があります。

トンネルではこれが難点です。エージェントはアウトバウンド(NATの背後にいるため)なので、エージェントはQUICとHTTP/3の*クライアント*です。一方、エッジはサーバーです。しかし、公開リクエストはエッジに届き、エッジはそれをエージェントにプッシュしたい。HTTP/3の仕様では、エッジはエージェントにリクエストストリームを開けません。

これを解決する設計例:

  1. カスタムアプリケーションプロトコルを使った生のQUICストリーム。 QUICはエンドポイントのどちらも双方向ストリームを開けるため、エッジは公開リクエストごとに1つ開き、独自のフレーミングを行います。これはHTTP/3 Extended CONNECTではなく、一般的なQUICの能力です。最もシンプルなトンネル向けの方法であり、cloudflaredはquic-go上にQUICトランスポートを構築しています。
  2. 事前に開いたExtended CONNECTストリームのプール。 エージェントはアイドル状態のExtended CONNECTストリームを複数開きます(例::protocol = tunnel-v1)。エッジはこれらを保持し、リクエストが来たら1つを取り出し、ターゲットサービス名を書き込み、バイトを中継します。エージェントはプレアンブルを読み取り、ローカルサービスに接続し、双方向にデータをコピーし、プールを補充します。これは標準のHTTP/3内で管理されますが、プール管理のコストがかかります。
  3. WebTransport over HTTP/3。 エージェントは:protocol = webtransportのExtended CONNECTリクエストを開き、そのセッション内でどちらもストリームを開けます。これはHTTP/3の未使用のサーバー起動ストリームの上に定義されているためです。ただし、ドラフト段階であり、変更の可能性があります。WebTransportは0RTTをサポートしません。
  4. 逆方向HTTP/3ドラフト。 HTTP/3の役割を逆にしたドラフトもあり、サーバーからクライアントへのリクエスト送信を可能にします。これらはドラフト段階で広く展開されていません。

現状のトンネルでは、選択肢1または2が実用的です。選択肢3は標準ブラウザ互換性を重視し、ドラフト段階を許容する場合に適します。

ストリーププールを使ったフロー(選択肢2)

   パブリッククライアント                エッジ(QUIC/H3サーバ)               エージェント(QUIC/H3クライアント)
        |                               |                                   |
        |                               |== QUICハンドシェイク、UDP 443 =======|
        |                               |== SETTINGS (ENABLE_CONNECT) =====|
        |                               |== CONNECT :protocol=tunnel-v1 ===|  ストリーム0(プール済み)
        |                               |== CONNECT :protocol=tunnel-v1 ===|  ストリーム4(プール済み)
        |  GET https://api.example.com  |                                   |
        |------------------------------|                                   |
        |                               |-- ストリーム0を取得し、「api」と送信 ----|
        |                               |                                   |-- 127.0.0.1:8080にダイヤル
        |---------- 応答 ----------|-------- ストリーム0のバイト -------|
        |                               |== エージェントが置き換えを開く =====|  ストリーム8

各公開リクエストは1つのQUICストリームにマッピングされます。50のサービス、または50の並行リクエストが1つのUDPソケットと1つのQUIC接続を共有します。


「50+」の実際の意味

50はプロトコルの制限ではなく、一般的なデフォルト値の範囲内の便利な数字です。quic-goではquic.Config.MaxIncomingStreamsのデフォルトは100の双方向ストリームです。それ以上の並行リクエストを見込む場合は、受信側で意図的に増やしてください。

quic-goのドキュメントには以下のフロー制御のデフォルト値が記載されています:

設定 デフォルト
InitialStreamReceiveWindow 512 KB
MaxStreamReceiveWindow 6 MB
InitialConnectionReceiveWindow 512 KB
MaxConnectionReceiveWindow 15 MB
MaxIncomingStreams 100

QUICはストリームごとと接続全体でフロー制御を行います。ストリーム制限は、サービスがバッファを過剰に消費しないようにし、接続制限は全体の容量を制御します。接続ウィンドウは、アクティブにデータを転送しているストリームの数に合わせて設定すべきです。小さすぎると、ストリーム間で競合し、スループットが低下します。

ホストのチューニング

QUICはユーザースペースのUDP処理で動作し、カーネルのUDPソケットバッファも重要です。quic-goはバッファを拡大しようとしますが、Linuxのrmem_maxやwmem_maxの制限に従います。推奨設定は次の通りです:

sysctl -w net.core.rmem_max=7500000
sysctl -w net.core.wmem_max=7500000

これによりバッファは約7.5MBに拡大されます。再起動後も有効にするには/etc/sysctl.d/に設定ファイルを置きます。Cloudflareのツールもこのバッファ設定を推奨しています。

###輻輳制御

BBRに切り替えるだけではありません。Linuxのtcp_congestion_control sysctlはTCPにのみ影響し、QUICの輻輳制御はQUICライブラリ内にあります。quic-goはCUBICを使用しています。開発者はBBRの実装予定はなく、BBRv3のサポートも未対応です。BBRが重要な場合は、使用しているQUICスタックがサポートする輻輳制御を確認してください。

スループットとTCP

QUICは常に高速とは限りません。ユーザースペースのパケット処理はCPU負荷が高く、一般的なカーネルにはTCPのオフロード機能が備わっています。高速・低損失の有線リンクでは、HTTP/3はHTTP/2と同じか、むしろ遅くなることもあります。特に損失や高遅延、モバイル環境では効果的です。自分のネットワークで測定してください。


技術比較

次元 SSHフォワーディング HTTP/2 over TCP HTTP/3 over QUIC
トランスポート TCP TCP UDP(QUIC)
マルチプレックス TCP内のチャネル、または別々の接続 1つのTCP接続内のストリーム ネイティブのQUICストリーム
損失の影響 全チャネル 全ストリーム 影響を受けたストリーム(ヘッダー圧縮依存も含む)
ハンドシェイク TCP + SSH鍵交換と認証 TCP + TLS 連携したトランスポートとTLS 1.3ハンドシェイク、0RTT
ネットワーク変更 接続が切れる 接続が切れる クライアント側のマイグレーションとパス検証
サーバー起動ストリーム あり(チャネル経由) なし(プッシュは非推奨、Extended CONNECTはクライアント起動) 可能(生のQUICで)、HTTP/3では不可。WebTransport経由も可能
ファイアウォール到達性 TCP TCP UDPが必要(ネットワークによってブロックされることも)

罠:UDPが常に利用できるわけではない

QUICトンネルはUDPが通ることが前提です。古い測定結果は古く、Googleの初期展開では約3~5%のネットワークでQUICがブロックされていました。企業ネットワークでは特に制限が厳しいです。いくつかのエンタープライズファイアウォールはUDP/443を意図的にブロックまたはレート制限し、検査ツールがTCPのトラフィックを観測できるようにしています。

したがって、TCPのフォールバックは必須です。cloudflaredのautoモードはこれを実現しており、QUICを優先し、UDP接続が確立できない場合はHTTP/2にフォールバックします。エージェントも同様に設定し、どのトランスポートがアクティブかを運用者に見せる必要があります。

また、UDPフローはNATタイムアウトでドロップされることもあります。アイドル状態のトンネルには、QUIC PINGフレームやアプリケーションのハートビートを送ることで、静かに死なないようにしましょう。


セキュリティとマルチテナントの分離

複数のサービスを1つの接続でマルチプレックスする場合、1つのストリームが侵害または誤設定された場合に他のテナントのリソースに到達しないようにする必要があります。

  1. 接続の認証を行い、ストリームだけでなく接続自体を認証。 最初のエージェント接続時に相互TLSやベアラートークンを使用します。0RTTは避け、早期データのリプレイを防ぎます。
  2. エージェントを所有するホスト名にバインド。 エッジはtenant-a.example.comを認証済みの接続にルーティングし、他のホスト名は許可しません。ホスト名の検証は必須です。
  3. エージェントの許可リストを使用。 サービス名を特定のローカルアドレスにマッピングし、それ以外は拒否します。特に、任意の宛先アドレスを選ばせてはいけません。これにより、サーバー側のリクエストフォージェリを防ぎます。
  4. テナントごとの同時接続数制限。 QUICのMAX_STREAMSを利用し、レートリミットやバイトクォータを設定します。これにより、1つのホスト名への攻撃や過負荷を防ぎます。
  5. 障害の分離。 1つのストリームのリセットは接続全体の切断につながらないようにします。プロトコル違反時の接続クローズはコネクションレベルで行います。

採用状況

HTTP/3はもはや珍しくありません。W3Techsは2026年3月時点で約38.6%、7月には約39.7%のウェブサイトがHTTP/3をサポートしていると報告しています。これらはサポートを表明しているサイトの割合です。実際のリクエストレベルの採用は低めです。Cloudflare Radarのデータによると、2026年第1四半期のグローバルリクエストの約21%がHTTP/3を使用しています。トンネルにとって重要なのは、ブラウザやCDN、主要なQUICライブラリ(quic-go、GoogleのQUICHEなど)が成熟していることです。


結論

50以上のローカルサービスを1つの接続で運用することは、すでに解決済みの問題です。成熟したトンネルは、多くのサービスを少数の接続上でマルチプレックスし、QUICはストリームごとの損失回復や単一のUDPポート、クライアント側の接続マイグレーションを可能にしています。重要なポイントは次の通りです:

  • HTTP/3のリクエストストリームはクライアント起動。 逆方向トンネルには、raw QUICストリーム、事前に開いたExtended CONNECTストリームのプール、またはWebTransportを使う必要があります。エッジが単にリクエストストリームを開くのは誤りです。
  • Extended CONNECT(RFC 8441、RFC 9220)は汎用的な仕組み。 WebSockets、UDP(RFC 9298)、IP(RFC 9484)、WebTransportを運びます。:protocolトークンは意図的に選びましょう。
  • UDPブロックを想定し、TCPフォールバックを計画。
  • チューニングと測定。 UDPバッファを増やし、並行性に合わせてフロー制御ウィンドウを設定し、自ネットワークでHTTP/2と比較して効果を測定してください。

参考資料

Continue from this article into the most relevant product guides and workflows.

Related Topics

#multiplexed tunnels#multi-tenant tunnels#HTTP/3 Extended CONNECT#HTTP/3 CONNECT method#run multiple services on one port#single TCP port tunneling#cloud ingress stream#microservices tunneling#HTTP/3 multiplexing#local services exposure#reverse proxy tunneling#developer tunneling tools#QUIC protocol tunneling#port multiplexing#bypass port bottlenecks#concurrent local services#HTTP/3 framing#cloud proxy ingress#secure local development#modern tunneling architecture#distributed microservices proxy#scalable tunneling solutions#high-performance tunneling#custom domain tunneling#localhost exposure tool#network address translation traversal#TCP stream multiplexing#zero trust tunneling#edge routing for developers#multi-service port sharing#dynamic service discovery tunnel#low latency tunneling#HTTP/3 transport layer#cloud native networking#containerized service tunneling#enterprise tunneling tools#developer infrastructure#secure ingress proxy#API gateway alternative#local webhooks testing#remote development environments#seamless port forwarding#advanced local tunneling#high concurrency tunnels#HTTP/3 web transport#network optimization tools#cloud tunnel management#developer productivity tools#backend service exposure#scalable cloud ingress#modern network tunneling#enterprise grade tunneling#InstaTunnel features#multi-tenant architecture

Keep building with InstaTunnel

Read the docs for implementation details or compare plans before you ship.

Share this article

More InstaTunnel Insights

Discover more tutorials, tips, and updates to help you build better with localhost tunneling.

Browse All Articles