同じ問題を違う設計で解く —— 競合トンネリングプロダクトを比較する
ngrok の答えは見えた。他社はどう解いている?
第 7 章までで、ngrok が「connect.ngrok-agent.com:443 への 1 本の TLS の上に muxado でストリームを多重化し、エッジで TLS を終端して x-forwarded-* を付けて転送する」という設計で問題を解いていることを、内側まで覗いてきた。第 6 章では同じ系統のプロトコルを手元で動かしてもいる。
ここで一度、視野を広げたい。
「localhost を世界に出す」という問題は、ngrok だけのものではない。Cloudflare Tunnel、frp、inlets PRO、bore、Tailscale Funnel、localtunnel ── 同じ問題に対して、それぞれ異なる答えを用意したプロダクトが現役で並走している。彼らは「ngrok の劣化版」ではなく、設計判断の組み合わせを変えることで違うユースケースに最適化した別の答え だ。
第 6 章で書いた mintunnel を思い出してほしい。あれは「muxado on TLS、エッジで TLS 終端、コントロールとデータを TypedStream で分離」という選択だった。だが、これは唯一解ではない。
この章では、6 プロダクトの設計を 3 つの軸で並べ、自分のユースケースに合うものを選べるようにする。
対象読者:第 7 章まで読み終え、ngrok の内部設計を理解した読者。他のトンネリングプロダクトとの差分を知って、選定眼を養いたい人。 読了時間:約 20 分
6 プロダクトを 1 段落ずつ眺める
Cloudflare Tunnel(cloudflared)
cloudflared デーモンが Cloudflare のグローバルエッジにアウトバウンドのみで複数の永続接続を張る方式。エッジは Anycast で世界中に分散していて、最も近いデータセンタに自動で吸い込まれる。多重化は既定が QUIC(UDP/7844)、フォールバックが HTTP/2(TCP/7844)。--protocol auto|http2|quic で選択可能で、QUIC を選べばトランスポート層で HoL ブロッキングが消える。TLS は Cloudflare エッジで終端し、エッジ〜オリジン間は cloudflared が張ったトンネル内で再暗号化される。クライアント cloudflared は Apache-2.0 の OSS だが、エッジとコントロールプレーンは Cloudflare の SaaS。「本番常用・Zero Trust 公開」の代表選手。
frp(fatedier/frp)
frps(公開 IP 側)と frpc(NAT 内)の 2 バイナリ構成。v0.10.0 から TCP ストリーム多重化を採用し、README は “like HTTP/2 Multiplexing” と表現している。トランスポートは TCP / KCP / QUIC / WebSocket から選べるため、QUIC を選べばトランスポート自体が多重化されるので二重に重ねないこともできる。v0.50.0 以降は TLS が既定で有効。公開ホスト名はワイルドカード DNS を frps に向け、subdomain 指定で <sub>.<subDomainHost> に動的払い出しできる。Apache-2.0 で全部 OSS。商用版はなく、SaaS は自分で建てる。中国語圏でのシェアが大きく、自前ホスティング派の事実上の標準になっている。
inlets PRO(OpenFaaS Ltd.)
「Cloud Native Tunnel」を標榜する Go 製。inlets-pro server(exit-server、公開 IP 側)と inlets-pro client(NAT 内)からなり、トンネル本体は WebSocket over TLS。Kubernetes Service type: LoadBalancer を private クラスタで実体化する inlets-operator が代表的な使われ方で、inletsctl がクラウドに 1 コマンドで exit-server をプロビジョニングする。初代 OSS の inlets はアーカイブ済み で、現行の inlets PRO は商用(EULA、JWT ライセンスキー必須)。ただし周辺ツール(operator・ctl・helm chart)は MIT。「SaaS 経由ではなく自社所有のエッジで公開したい」要件にはまる。
bore(ekzhang/bore)
Rust + Tokio で書かれた約 400 行の極小実装。サーバはコントロールポート TCP/7835 で待ち、クライアントが “Hello” メッセージで公開したいポートを要求する。ストリーム多重化はしない ── コネクションごとに新規 TCP ストリームをサーバに張り、UUID で識別する。既定では一切暗号化しない。シークレットは初回ハンドシェイクの HMAC チャレンジレスポンスだけで使われ、以後の通信は素のまま流れる。公開ホスト名はポートベース(bore.pub:<PORT>)。MIT の完全 OSS。「読めば 1 時間で全部分かる」教育的な小ささ が魅力で、デモ・CI のデバッグ・Webhook の一時受信に向く。
Tailscale Funnel
Tailscale(WireGuard ベースの mesh VPN)の上で動く公開トンネル機能。Funnel ingress は DERP リレーと同様に georeplicated された Tailscale 社のフロントエンドで、TCP を受けると peerapi の HTTP リクエストとして対象ノードに「SNI 名と src IP:port」を通知し、実際のデータ転送はノード〜ingress 間の Tailscale(WireGuard)トンネルを流れる。TLS は Funnel ingress で終端せずパススルー し、ノード上で Caddy 等が終端する。ポートは 443 / 8443 / 10000 に限定。公開ホスト名は MagicDNS 名(<machine>.<tailnet>.ts.net)の固定値。Tailscale クライアントは BSD-3-Clause、コアは商用 SaaS。「既に Tailscale を入れている人のついで公開」が刺さる。
localtunnel(localtunnel/localtunnel)
Node.js 製の古典。localtunnel.me という公式公開サーバが現役で、npx localtunnel --port 3000 で ランダムサブドメイン(<random>.loca.lt)が払い出される。実装上はリクエストごとにクライアント→サーバの追加 TCP コネクションを張るプールモデルと推定 される(要追加検証 ── README に多重化方式の明記はなく、localtunnel/server のソース確認が必要)。認証なしで誰でも使え、TLS は localtunnel サーバ側で終端する。ngrok 以前から存在する系譜のひとつで、MIT の完全 OSS。デモ・Webhook 受信向きで、本番には非推奨。
比較表 ── 7 軸で並べる
ここまでの 6 プロダクトと ngrok を、設計上の差分が出やすい 7 軸で並べる。第 1 章から繰り返し書いてきた通り、ngrok の多重化は muxado on TLS であって yamux ではない。frp が(独自の TCP ストリーム多重化として)yamux 系の実装を使っているのと混同しないでほしい。
| 軸 | ngrok | Cloudflare Tunnel | frp | inlets PRO | bore | Tailscale Funnel | localtunnel |
|---|---|---|---|---|---|---|---|
| 多重化 | muxado on TLS | QUIC(既定)/ HTTP/2(fallback) | 独自 TCP ストリーム多重化+ TCP/KCP/QUIC/WS 選択 | WebSocket over TLS 上で多重化 | なし(コネクションごと新規 TCP) | Tailscale (WireGuard) パススルー | プールモデル(要追加検証) |
| コントロールプレーン | 中央集権・Anycast SaaS | 中央集権・Anycast SaaS | 自前ホスティング | 自前 exit-server | 自前 or bore.pub 単一サーバ | 中央集権 SaaS + georeplicated ingress | 単一サーバ |
| 認可 | API authtoken(ACL バインド付き) | Tunnel Token+ Access (OIDC/SAML) | 共有トークン or OIDC | 共有トークン+ JWT ライセンス | オプションの HMAC シークレット | Tailscale SSO/OIDC + Policy File | なし |
| TLS 終端 | エッジ終端 | エッジ終端 | エッジ終端、トンネル自体も TLS | エッジ(exit-server)終端 | 暗号化なし(既定) | 終端しない(パススルー) | エッジ終端 |
| 公開ホスト名 | ランダム / 予約 / カスタムドメイン | カスタムドメイン中心(CNAME) | ワイルドカード <sub>.<domain> 動的+カスタム | ユーザー所有ドメインを exit-server に向ける | ポートベース | MagicDNS 固定、443/8443/10000 のみ | ランダムサブドメイン |
| OSS 境界 | クライアント一部 OSS、コアは SaaS | クライアント Apache-2.0、エッジ SaaS | Apache-2.0 で全部 OSS | 商用(周辺ツールのみ MIT) | MIT 完全 OSS | クライアント BSD-3、コア SaaS | MIT 完全 OSS |
| 代表ユースケース | 開発・本番・Webhook 全般 | 本番常用・Zero Trust 公開 | 自前 SaaS・自宅鯖・社内 LAN | K8s LoadBalancer 代替・自社エッジ | デモ・一時公開 | 既存 Tailscale 利用者のついで公開 | デモ・Webhook 受信 |
設計軸の解釈 ── 3 つの切り口で読む
表を眺めるだけだと「みんな違ってみんないい」で終わってしまう。本章の眼目は、設計上どこに分岐があるか を 3 軸に圧縮して読み解くことだ。
設計軸 1:多重化方式 ── QUIC / HTTP/2 / 独自 / なし
mindmap
root((多重化方式の選択))
QUIC ベース
Cloudflare Tunnel(既定)
frp(選択肢のひとつ)
HTTP/2 ベース
Cloudflare Tunnel(fallback)
独自プロトコル
ngrok(muxado on TLS)
frp(独自 TCP stream mux)
inlets PRO(WebSocket over TLS 上の多重化)
多重化しない
bore(接続ごとに新規 TCP)
Tailscale Funnel(WireGuard パススルー)
localtunnel(プールモデル・要追加検証)
第 4 章で muxado を入れたとき、私たちは「1 本の TLS の上に複数の論理ストリームを乗せる」という設計判断を採用した。これは「コネクション確立コストを最初の 1 回だけにする」「ストリーム単位で軽くオープン/クローズできる」というメリットがある反面、TCP の上で多重化するため HoL ブロッキングが残る という代償も背負う。
ngrok と frp と inlets PRO は、それぞれ 独自プロトコル系の多重化 を選んだ。muxado は HTTP/2 のフレーミング層から HEADERS / SETTINGS / PING / PUSH_PROMISE / PRIORITY を全部削った subset で、frp は yamux 系の独自実装、inlets PRO は WebSocket over TLS の上に独自層を被せている。設計の好みはそれぞれだが、「軽量で読めるサイズ」を狙う点は共通だ。
Cloudflare Tunnel は QUIC を既定 にした唯一の選手で、UDP 上で動かすぶん HoL ブロッキングが原理的に消える。逆に bore は「多重化なんていらない、コネクションごとに新規 TCP を張れば十分」と割り切った。読めば 1 時間で全部分かる小ささを優先した結果だ。Tailscale Funnel に至っては「TCP の上の多重化」というレイヤーをそもそも持たない。下に WireGuard がいるので、そこに任せれば済む。
設計軸 2:TLS 終端位置 ── エッジ終端 / 終端しない / 暗号化なし
quadrantChart
title TLS 終端位置とコントロールプレーン分散度
x-axis "TLS: 暗号化なし" --> "エッジ終端"
y-axis "コントロールプレーン: 単一/自前" --> "Anycast/分散 SaaS"
quadrant-1 "分散 SaaS + エッジ終端"
quadrant-2 "分散 SaaS + 終端しない"
quadrant-3 "単一/自前 + 終端しない"
quadrant-4 "単一/自前 + エッジ終端"
ngrok: [0.85, 0.9]
"Cloudflare Tunnel": [0.9, 0.95]
"Tailscale Funnel": [0.15, 0.8]
frp: [0.7, 0.2]
"inlets PRO": [0.75, 0.25]
localtunnel: [0.75, 0.35]
bore: [0.05, 0.15]
TLS の扱いは 3 つに割れる。
エッジ終端派(ngrok・Cloudflare Tunnel・frp・inlets PRO・localtunnel)は、エッジ側が証明書を持ち、クライアントから来る HTTPS リクエストを復号して内側に流す。ユーザーの目線では「証明書を自分で用意しなくていい」という最大のメリットがあり、これが ngrok を ngrok http 3000 の 1 コマンドで魔法に見せている源泉でもある。
終端しない派(Tailscale Funnel)は、Funnel ingress を TCP パススルーにしてノード側で TLS を終端させる。これは「Tailscale の中に閉じた WireGuard 経路を通る」という前提があるからこそ取れる設計で、E2E で「Tailscale 社のサーバですら平文を見られない」ことを保証する。代わりに、ユーザーはノード側で Caddy 等を立てて TLS を扱う責任を負う。
暗号化しない派(bore)は最も尖った選択だ。「素の TCP を流す。TLS が必要ならアプリ側で被せろ」と言い切っている。これが許されるのは bore が「本番に使うべきでない、デモ・一時公開用」と最初から自己定義しているからで、設計の整合性は取れている。
設計軸 3:OSS / 商用境界 ── クライアント OSS / 全 OSS / 商用 / 周辺ツールのみ OSS
| 境界の引き方 | 該当プロダクト | 設計の意図 |
|---|---|---|
| クライアントのみ OSS、コアは SaaS | ngrok・Cloudflare Tunnel・Tailscale Funnel | エッジの運用コストと信頼性で課金、クライアントは透明化して採用障壁を下げる |
| 全部 OSS | frp・bore・localtunnel | 「自分で建てる前提」。SaaS を提供しない or 公式公開インスタンスは best-effort |
| 商用本体+周辺ツール OSS | inlets PRO | エコシステム(operator・helm)は OSS、本体は JWT ライセンスで売る |
ここはビジネスモデルの違いがそのまま設計に出ているところだ。ngrok・Cloudflare Tunnel・Tailscale Funnel は「エッジを世界に持つ」こと自体に価値を集中 させていて、Anycast / GSLB / georeplicated ingress といったインフラ投資が課金根拠になる。だからクライアントは積極的にオープンソース化して、採用障壁を下げにいける。
一方、frp・bore・localtunnel は「コード本体だけが価値」 で、SaaS インフラを提供しないかわりに全部 OSS という形を取る。frp が中国語圏で広く使われているのは「自分のサーバを自分で運用する文化」と相性が良いからで、技術的優劣だけでは説明しきれない。
第 6 章で書いた mintunnel を商用化するとしたら、私たちはこの 3 つのどこに着地するべきだろうか。ngrok 側に寄せて Anycast エッジで戦うなら巨大資本が要る。frp 側に寄せて全部 OSS で出すなら、収益は別の経路(コンサル・サポート)で取る必要がある。inlets PRO 型の「周辺 OSS + 本体商用」は、その中間として歴史的に検討されてきた選択肢だ ── 設計判断には選択肢があり、その選択がそのまま事業構造を規定する。
ユースケース別の選び方
設計軸を理解した上で、自分のユースケースから引ける表に落とし込もう。
| 要件 | 第 1 候補 | 第 2 候補 | 避けるべき |
|---|---|---|---|
| 常用本番で Zero Trust 公開したい | Cloudflare Tunnel | ngrok(Edge + Traffic Policy) | bore・localtunnel |
| Webhook をローカルで受けたい(開発時) | ngrok | localtunnel | Tailscale Funnel(ポート制限) |
| デモ URL を 5 分で配りたい | ngrok / localtunnel | bore | inlets PRO(商用ライセンス必要) |
| 自宅サーバを社内 LAN 風に公開したい | frp | Cloudflare Tunnel | bore(暗号化なし) |
| 完全に自前ホスティングしたい(SaaS 依存ゼロ) | frp | inlets PRO(exit-server 自前) | ngrok・Cloudflare Tunnel・Tailscale Funnel |
| 既に Tailscale を入れていて「ついで」で公開したい | Tailscale Funnel | ─ | ─ |
K8s で type: LoadBalancer を private クラスタで実体化したい | inlets PRO(inlets-operator) | Cloudflare Tunnel | bore・localtunnel |
| コードを読んで仕組みを学びたい | bore(約 400 行) | frp(Apache-2.0) | ngrok(コアはクローズド) |
以下のフローチャートでも引ける。
flowchart TD
Start[localhost を世界に出したい]
Start --> Q1{用途は本番常用?}
Q1 -->|Yes| Q2{SaaS 依存 OK?}
Q1 -->|No 開発・デモ| Q3{Webhook 受信?}
Q2 -->|Yes| CF[Cloudflare Tunnel]
Q2 -->|No 自前ホスティング| FRP[frp]
Q3 -->|Yes| NG[ngrok]
Q3 -->|No デモ URL| Q4{TLS 必須?}
Q4 -->|Yes| LT[ngrok / localtunnel]
Q4 -->|No 一時的でいい| BORE[bore]
このフローはあくまで「典型的な選び方の一例」だ。実務では制約条件(既存スタック、コンプラ要件、運用体制)が絡むため、最終的には 3 軸(多重化方式・TLS 終端位置・OSS 境界)に立ち戻って自分で判断する必要がある。
ngrok の位置取りを再確認する
ここで第 6 章の mintunnel と本物の ngrok を、本章の 3 軸に当てはめて再確認しておく。
| 軸 | mintunnel(第 6 章) | 本物の ngrok |
|---|---|---|
| 多重化 | muxado on TLS(自作) | muxado on TLS(GA) |
| TLS 終端 | エッジ終端(自作証明書) | エッジ終端(GSLB で最寄り PoP) |
| OSS 境界 | 全部自作 | クライアント一部 OSS、コアは SaaS |
mintunnel は「ngrok と同じ設計軸の選択」を、教育用に最小実装したものだ。本物の ngrok との差は、本章で見た他社との差より遥かに小さい。一方、Cloudflare Tunnel の QUIC ベース、Tailscale Funnel の TLS パススルー、bore の暗号化なし は、軸そのものの選び方が違う。設計判断には選択肢があり、その選択がそのまま「誰のための、どんな問題を解くプロダクトか」を規定する。
そして当然、これらのプロダクトは止まっていない。Cloudflare Tunnel は QUIC 経路で 2026 年現在ポスト量子暗号鍵交換を既定にしつつあり、ngrok は Agent Endpoint と Traffic Policy で「トンネルというより L7 ゲートウェイ」に拡張しつつあり、Tailscale Funnel はポート制限と双方向性の改善を続けている ── では、これらのプロダクトはどこへ向かうのか。次章のエピローグで、その先を展望する。
章末まとめ
- localhost を世界に出す同じ問題に、6 プロダクトはそれぞれ違う設計で答えている
- 設計の分岐は 多重化方式 / TLS 終端位置 / OSS 境界 の 3 軸に圧縮できる
- ngrok の多重化は muxado on TLS。frp の独自 TCP stream mux(yamux 系)と混同しないこと
- 「本番常用なら Cloudflare Tunnel / ngrok」「自前ホスティングなら frp」「デモなら bore / localtunnel」「Tailscale 利用者なら Funnel」が大まかな選定軸
- 第 6 章の
mintunnelは ngrok と同じ軸選択の最小実装。設計判断には選択肢があり、選択がそのまま事業構造とユースケース適合度を規定する- 次章では、これらのプロダクトがどこへ向かうかをエピローグとして展望する