目次を表示する

ngrokの魔法を解き直す ── 仕組みを理解し、Goで自作してみる

同じ問題を違う設計で解く —— 競合トンネリングプロダクトを比較する

同じ問題を違う設計で解く —— 競合トンネリングプロダクトを比較する

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 系の実装を使っているのと混同しないでほしい。

ngrokCloudflare Tunnelfrpinlets PROboreTailscale Funnellocaltunnel
多重化muxado on TLSQUIC(既定)/ 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、エッジ SaaSApache-2.0 で全部 OSS商用(周辺ツールのみ MIT)MIT 完全 OSSクライアント BSD-3、コア SaaSMIT 完全 OSS
代表ユースケース開発・本番・Webhook 全般本番常用・Zero Trust 公開自前 SaaS・自宅鯖・社内 LANK8s 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、コアは SaaSngrok・Cloudflare Tunnel・Tailscale Funnelエッジの運用コストと信頼性で課金、クライアントは透明化して採用障壁を下げる
全部 OSSfrp・bore・localtunnel「自分で建てる前提」。SaaS を提供しない or 公式公開インスタンスは best-effort
商用本体+周辺ツール OSSinlets 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 Tunnelngrok(Edge + Traffic Policy)bore・localtunnel
Webhook をローカルで受けたい(開発時)ngroklocaltunnelTailscale Funnel(ポート制限)
デモ URL を 5 分で配りたいngrok / localtunnelboreinlets PRO(商用ライセンス必要)
自宅サーバを社内 LAN 風に公開したいfrpCloudflare Tunnelbore(暗号化なし)
完全に自前ホスティングしたい(SaaS 依存ゼロ)frpinlets PRO(exit-server 自前)ngrok・Cloudflare Tunnel・Tailscale Funnel
既に Tailscale を入れていて「ついで」で公開したいTailscale Funnel
K8s で type: LoadBalancer を private クラスタで実体化したいinlets PRO(inlets-operator)Cloudflare Tunnelbore・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 と同じ軸選択の最小実装。設計判断には選択肢があり、選択がそのまま事業構造とユースケース適合度を規定する
  • 次章では、これらのプロダクトがどこへ向かうかをエピローグとして展望する