エピローグ:トンネリングの未来を展望する
プロローグの問いに、もう一度向き合う
第 1 章で、こんな 3 つの問いを置いた。
- なぜ、ファイアウォールの内側にあるサーバーが、外から叩けるのか。
- なぜ、自分は何も設定していないのに、有効な TLS 証明書付きの公開 URL が手に入るのか。
- なぜ、
ngrokプロセスを止めた瞬間に、その URL は消えるのか。
ここまでのシリーズで、これらは「魔法」ではなくなったはずだ。同じ問いに、第 9 章まで進んだ自分の言葉で答え直してみよう。
問い 1:なぜファイアウォール内のサーバーが叩けるのか
答え:NAT/ファイアウォールの非対称性(conntrack の片方向性)を、リバーストンネルが逆手に取っているから。
第 2 章で見たように、家庭ルーターや企業ファイアウォールは、内側から外への接続要求が来た瞬間に conntrack table へエントリを書き、その戻りトラフィックだけを通す。外から内への新規セッションは、原則すべて落とす。だから普段、私たちのラップトップは外から見えない。
ngrok(および本シリーズで作った mintunnel)が突破に使ったのは、この壁を「壊す」のではなく「裏返す」発想だった。agent が connect.ngrok-agent.com:443 へ outbound で TLS 接続を 1 本張る。その 1 本は、ルーターから見れば普通の HTTPS 通信で、conntrack のエントリも普通に作られる。あとは、その 1 本のチャネルの中で、エッジ側から「新しいリクエストが来た」という通知を受け取り、agent が outbound 方向にレスポンスを書き戻す。インバウンドのように見えるトラフィックを、すべて「既存の outbound 接続の上の双方向ストリーム」として運ぶ。これがリバーストンネルの本質だ。
そして第 2 章の末尾で、伏線として名前だけ覚えてもらった用語がある ── RFC 4787 の Endpoint-Independent Mapping。あれが、ここで効いてくる。リバーストンネル方式が安定して動くためには、agent が 1 度確立した外部マッピングが、その後の双方向通信の間ずっと同じ (外部 IP, 外部 Port) で維持される必要がある。これを保証するのが、まさに RFC 4787 が REQ-1 として現代の NAT に義務付けたマッピング動作だった。
Symmetric NAT(Address-and-Port-Dependent Mapping)相手だとホールパンチングが破綻するのは第 2 章で触れたとおりだが、リバーストンネル方式は Symmetric NAT 配下でも動く。なぜなら、宛先(ngrok エッジ)が 1 つしかなく、その 1 つに対する外部マッピングだけが安定していればよいからだ。NAT 越えとして見たとき、リバーストンネルが「最も汎用」と呼ばれる理由はここにある。Endpoint-Independent Mapping という抽象は、それ自体は UDP の話だが、TCP の conntrack でも同じ性質(既存セッションのマッピング保持)が成立する以上、ngrok 型の設計はその上に綺麗に乗る。
問い 2:なぜ TLS 証明書が手に入るのか
答え:エッジ側がワイルドカード証明書を持ち、サブドメイン単位で公開 URL を払い出して TLS 終端しているから。
ローカル開発機で TLS を取ろうとすると、HTTP-01 チャレンジは NAT 配下では成立せず、DNS-01 はドメイン所有と DNS API 自動化が要る。第 8 章で見た自前 SSH -R + 自前ドメイン + Caddy の構成では、これらを自分で組み上げる必要があった。
ngrok エッジが解いているのは、ここをサービス側に肩代わりさせる、というシンプルな問題分割だ。*.ngrok-free.app のような形でワイルドカードを公開 CA から取得しておき、agent が endpoints を作るたびに「あなたのトンネルはこの FQDN を割り当てます」と通知する。トラフィックは PoP で TLS 終端し、内部では平文(だが既存の muxado over TLS の中)で agent に流れる ── HTTPS endpoint のデフォルト動作だ。
ただしこの「PoP で終端」は唯一の答えではなく、Traffic Policy の terminate-tls アクションで「edge で終端」「agent で終端」「upstream で終端」を選べる。TLS endpoint はデフォルト pass-through で、SNI ルーティングだけ行って暗号文のまま agent に渡す。「証明書をどこで持つか」という判断を、サービスではなくユーザーが宣言的に書ける ようになっているのが、近年の ngrok の特徴的な進化点だった。
問い 3:なぜプロセスを止めると URL が消えるのか
答え:Agent Endpoint が agent プロセスのライフサイクルに紐づいているから。
第 7 章で触れた Cloud Endpoint と Agent Endpoint の対比を思い出してほしい。Agent Endpoint は「agent プロセスが ngrok cloud service と接続している間だけオンライン」で、「agent が authoritative、ダッシュボード/API は read-only」という整理だった。
agent が SIGINT を受けて TLS コネクションを閉じると、その接続を通じて生きていたすべての endpoint がエッジ側で expire する。*.ngrok-free.app の DNS レコードは残るかもしれないが、エッジルーターは「このホスト名に対応する agent はもういない」と判断し、新規リクエストには 4xx/5xx を返す。これが「URL が消える」の正体だ。
逆に Cloud Endpoint は agent に依存しない常駐型で、ngrok クラウド側で完結する。これがあると「agent を再起動しても URL は維持される」という運用が可能になる。「URL が消える/消えない」は、トンネルの実装の癖ではなく、設計判断として明示的に分けられた抽象 になっている。プロローグで「魔法のように消える」と書いた挙動は、つまり Agent Endpoint の意図された設計そのものだった。
80 行のコードが、200 行で本物に届いた話
第 3 章で書き始めた mintunnel は、当初 80 行だった。mintunnel-server 40 行 + mintunnel-agent 40 行。1 つの outbound TCP の上を、1 つのリクエストだけが行き来する、極端に痩せた実装だった。
それを毎章 1 つずつ太らせた。
- 第 4 章で muxado を導入し、1 本の TCP に複数の会話を同時に流せるようにした。
net.Listenerがそのまま受け取れる API で、Go のnet/httpがそのまま乗った瞬間に「これは確かに本物の系統だ」と分かった。 - 第 5 章でホスト名ベースのルーティングと
x-forwarded-*ヘッダー書き換えを足した。*.ngrok-free.appから受けたリクエストを、対応する agent にだけ届ける ── ngrok エッジが実際にやっていることの、骨格だけの再現だった。 - 第 6 章で
TypedStreamSessionを被せ、コントロールチャネル(authentication、bind、heartbeat)とデータチャネル(実際のリクエスト/レスポンス)を 1 本の TLS 接続の中で型 ID で区別できるようにした。ここに来た時点で、コード行数は 200 行に届くか届かないかというところで、しかし扱っている概念は本物の ngrok の agent ↔ edge プロトコルとほぼ同じ系統になっていた。
muxado v2 の TypedStreamSession は、ngrok 公式が golang.ngrok.com/muxado/v2 として 2024-10 にリリースし、現役でメンテし続けている本物のライブラリだ。私たちが手元で書いたコードは、まさにそのライブラリを、本家と同じ使い方で呼んでいた。「自作してみたけど、結局おもちゃでしかない」ではなく、「自作してみたら、本物の core にたどり着いた」 ── これが、このシリーズが目指していた到達点だった。
第 7 章での答え合わせで、自作版に足りないものも分かった。GSLB によるマルチ PoP 分散、Traffic Policy という宣言的設定言語、Cloud Endpoint と Agent Endpoint の抽象、Kubernetes Operator ── これらは「core はそのままに、その周りを十数年かけて磨き上げてきた層」だった。逆に言えば、core は 200 行で書ける。本物のサービスとの距離は、core の部分ではなく、運用と抽象化の部分にある。これを実感として持てたなら、このシリーズの第 1 のゴールは達成されている。
ここから先、トンネリングはどこへ向かうか
トンネリングという技術自体は、リバーストンネルというパターンが 1990 年代の SSH -R で確立した時点で「枯れた」と言える。だが 2020 年代後半に入って、その「枯れた技術」が、ここ最近の AI / Zero Trust の文脈で再び中心に出てきている。最後に、その地平を 3 つの方向で見ておきたい。
MCP / AI Gateway ── LLM が「外部の自分」を呼ぶ時代
2025〜2026 年で最も大きな変化は、ngrok が MCP(Model Context Protocol)サーバーの前段 として使われるパターンの台頭だ。Claude や OpenAI から、開発者のローカルにある MCP サーバーを呼ばせたい ── そのとき必要なのは結局、「ローカルマシンを安全に公開する」という、まさに ngrok が 13 年前から解いてきた問題だった。
公式ガイド Using ngrok as your MCP gateway が示すパターンは興味深い。ローカル MCP サーバーの前に Internal Agent Endpoint を立て、それを Cloud Endpoint が参照し、Cloud Endpoint 側に Traffic Policy で「Authorization ヘッダー検証」「IP Intel フィルタリング」「トークン単位の rate limit」を強制する。LLM が叩く API として安全に運用するには、トンネリング層と「ポリシーエンフォースメント層」が一体になっている必要がある ── というのが、この設計に込められた主張だ。
さらに 2026 年版の AI Gateway は、ngrok 公式ブログによれば「2025 が prompt routing 中心だったのに対し、2026 は session-aware orchestration」へ進んでいる。1 つのユーザリクエストが 20〜50 の LLM call と tool 呼び出しを誘発する世界で、OpenAI / Anthropic / fine-tuned / local model を横断するルーティング、cost-based selection、PII redaction、prompt injection 検知が、トンネルと同じレイヤーに統合されつつある。2026-02-18 には Native Anthropic SDK サポートが入り、prompt caching と extended thinking がそのまま通る。トンネルが「単に到達性を提供する」道具から、「AI トラフィックの policy enforcement plane」へと役割を広げているのが、この時期の特徴だ。
Zero Trust ネットワークアーキテクチャの中での位置づけ
もう 1 つの大きな潮流が Zero Trust だ。「境界モデルではなくアイデンティティと文脈で都度認可する」という考え方が、企業ネットワークの再構築軸として定着した結果、「特定のホストに到達できるかどうか」よりも「誰が、どの認可で、どのトラフィックを流せるか」の方が一次的な関心事になった。
ngrok の Traffic Policy がやっているのは、まさにこの方向の問題定義の置き換えだ。jwt-validation や OIDC、rate-limit、add-headers、terminate-tls を YAML/JSON で宣言する ── これらは Zero Trust の語彙そのものを、agent と edge の間に挟まる薄い policy layer に落とし込んだものだ、と読める。第 6 章で見た「コントロールチャネルとデータチャネルの分離」が、より大きな視点で見ると「policy plane と data plane の分離」の最小版だった、という言い方もできるだろう。
トンネリングが Zero Trust の中で占める位置は、おそらく「アイデンティティで保護された L7 トンネル」 ── 言い換えると、SSO / IdP と接続された、認可ベースの公開エンドポイントだ。VPN が「ネットワーク到達性を一括で渡す」発想だったのに対し、これは「個別の HTTP リクエストごとに policy で評価する」発想で、後者の方がアプリケーション層への視点が強い。ngrok の Cloud Endpoint / Agent Endpoint と Traffic Policy の組み合わせは、その方向の参考実装として現時点で最も完成度が高いものの 1 つだ。
QUIC ベースの新世代
トランスポート層の話としては、QUIC が次の主役になりつつある。第 8 章で見たように、Cloudflare Tunnel はすでにデフォルトのプロトコルとして QUIC を採用 していて、HTTP/2 は fallback の位置に下がっている。
QUIC の何がトンネリング用途で効いてくるかは、いくつかの軸で整理できる。
- 接続の確立が速い:0-RTT / 1-RTT で済むため、ハートビートや再接続のコストが TLS over TCP より明確に低い。
- HoL ブロッキングがない:muxado を含む TCP の上の多重化プロトコルが避けられなかった「同じ TCP 接続上のあるストリームのパケットロスが、他のストリームの配送も遅らせる」問題が、QUIC では構造的に解消される。第 4 章で muxado のフロー制御が抱えていた制約が、ここで初めてトランスポート層から取り払われる。
- コネクションマイグレーション:クライアント IP が変わっても connection ID で同一セッションを維持できる。モバイル端末や Wi-Fi/4G 切り替えのシナリオに強い。
ngrok 自身が agent ↔ edge を QUIC ベースに切り替えるかは 2026-05 時点で公式アナウンスがなく、golang.ngrok.com/muxado/v2 が現役でメンテされている以上、当面は TLS over TCP の上の muxado が中核に残ると見て良さそうだ。だがこの分野全体としては、Cloudflare Tunnel が先行した QUIC ベースの実装が、次の 3〜5 年で標準的な選択肢に上がっていくのは間違いないだろう。muxado の Frame 4 種のシンプルさに惹かれて読んできた読者は、QUIC のストリーム抽象を見ると、設計の系譜として連続したものを感じ取れるはずだ ── どちらも HTTP/2 のフレーミング層から派生し、ヘッダー圧縮や PUSH を捨てて純粋な多重化に絞った設計、という意味で同じ家系にいる。
次に読むべきもの
このシリーズで「core はだいたい押さえた」と感じてもらえたとして、ここから先の方向は読者ごとに分かれる。少しだけ案内を残しておきたい。
- TLS 終端と ACME 自動化を自分で組み上げたい人 ── 付録 A「TLS 終端と SNI ルーティングを実装する」を読んでほしい。本編で意図的に省いた「公開 URL に有効な証明書を貼る」最後の一歩を、Traffic Policy の
terminate-tlsの動作と、SNI ベースのルーティングを軸に解説している。 - muxado 以外の選択肢を試したい人 ── 付録 B「同じトンネルを yamux と HTTP/2 で書き直す」を読んでほしい。第 4 〜 6 章で muxado を使って書いたコードを、HashiCorp の
yamuxおよび Go 標準のnet/httpの HTTP/2 で書き直すと、どこが揃いどこが変わるかが見える。muxado の設計判断(HEADERS / PUSH を削った理由など)が、対比の中ではじめてくっきり見えてくる。 - ngrok の運用を深掘りしたい人 ── ngrok 公式 docs の
agent/traffic-policy/universal-gatewayの 3 セクションを順に読むのが王道だ。本シリーズで触れた抽象(Cloud Endpoint / Agent Endpoint / Traffic Policy)が、実際の YAML や CLI でどう書かれるかは公式が一番濃い。 - トランスポートの仕様まで降りたい人 ── RFC 4787(BCP 127)、RFC 5128、RFC 4254 §7.1 を順に読むと、第 2 章と第 3 章で扱った「NAT の非対称性 → リバーストンネル → SSH
-R」の地続きの理屈が、IETF の原文で確認できる。
旅の終わりに
第 1 章で「魔法を解く旅」と呼んだものは、これで終わる。ターミナルで ngrok http 3000 と打ったとき、ファイアウォールの中で何が起きているか ── conntrack のどのエントリが書かれ、connect.ngrok-agent.com:443 で何が握られ、muxado の TypedStreamSession のどの型 ID が往復しているか、PoP のどこで TLS が終端され、x-forwarded-* がどの順番で積まれているか ── このシリーズを書いた側として、それが少なくとも「魔法」ではなくなったと言ってもらえたら、十分に成功と呼んでいい。
そして、ここから先は終わらない。MCP の登場で AI が外部の自分を呼ぶ必要が出てきたり、Zero Trust の進化でトンネルが認可レイヤーを担うようになったり、QUIC が TCP 上の多重化を構造的に塗り替えたり ── トンネリングという「枯れた」技術は、その上にこれからも新しい層が積まれていく。「ngrok http 3000」というたった 1 行のコマンドの背後にある世界は、これからも十分に面白い場所のままだ。
良い旅を。
参考文献
本シリーズで実際に本文中で参照・引用した一次情報を以下にまとめる。
ngrok 公式ドキュメント・ブログ
- ngrok docs — Agent
- ngrok docs — Agent Config Version 3
- ngrok docs — Agent Endpoints
- ngrok docs — HTTP/S Agent Endpoints
- ngrok docs — Traffic Policy Overview
- ngrok docs — Using ngrok as your MCP gateway
- ngrok docs — TLS Routing
- ngrok blog — Terminate TLS however you want
- ngrok press release — GSLB Launch
- ngrok blog — Pin your traffic to specific regions
- ngrok blog — Secrets for Traffic Policy GA
- ngrok blog — AI gateways in 2026
- ngrok blog — Native Anthropic SDK support
- ngrok blog — ngrok-operator multiple installs and uninstall
muxado / ngrok-go ライブラリ
- pkg.go.dev — golang.ngrok.com/muxado/v2
- pkg.go.dev — golang.ngrok.com/ngrok/v2
- GitHub — inconshreveable/muxado
- GitHub — ngrok/ngrok-go
創業者・歴史的経緯
- The Changelog Podcast #210 transcript — Alan Shreve on ngrok and muxado
- Twilio Blog — How Alan Shreve built ngrok with Go
- inconshreveable Projects
関連 RFC(NAT / トンネリング / SSH)
- RFC 2663 — IP Network Address Translator (NAT) Terminology and Considerations
- RFC 3489 — STUN(初版・Cone 分類定義、後に廃止)
- RFC 4254 — The Secure Shell (SSH) Connection Protocol(
tcpip-forward定義) - RFC 4787 — NAT Behavioral Requirements for Unicast UDP(BCP 127)
- RFC 5128 — State of Peer-to-Peer (P2P) Communication across NATs
- RFC 5389 — Session Traversal Utilities for NAT (STUN, 改訂版)
- RFC 8445 — Interactive Connectivity Establishment (ICE)
- RFC 8656 — Traversal Using Relays around NAT (TURN)
競合プロダクト
- Cloudflare Tunnel docs
- GitHub — fatedier/frp
- GitHub — ekzhang/bore
- Tailscale Funnel docs
- GitHub — inlets/inlets-pro