- 外部サービスから Webhook を受信したい。
- ただ、グローバル IP があっても、内向きのアクセス経路を作れない(社内 LAN のルーター設定は触れない)。
社内ネットワークの外から見るとグローバル IP はあるものの、社内の PC や VM へはルーターのポート転送(NAT)がないと届きません。ルーター設定を触れない環境では、インターネットからローカルへ直接アクセスする経路を自分では作れない、という状態になります。
加えて Webhook について少し書くと、Webhook は、GitHub・Stripe・SendGrid などの外部サービスが、こちらの URL に HTTPS で POST してくる仕組みです。つまり「こちらから API を叩く」のではなく、「相手からこちらに届く」必要があります。 localhost や 192.168.x.x の URL は外部から見えないため、そのままでは Webhook を受け取れません。
このような場合にどう対応する方法があるものかとネットで調べてみたところ、渡りに船で見つけたサービスが Cloudflare Quick Tunnels です。今回は、社内 LAN 上の Vagrant + VMware Fusion 環境で 外部サービスの Webhook を受信するための手順を書いてみようと思います。
まず、Cloudflare Quick Tunnels についてどのようなものかを書いてみたいと思います。
公式の図と説明もあるのでリンクをつけましたが、 Cloudflare Quick Tunnels は、ローカルに cloudflared を置き、サーバー側から Cloudflare へ外向きに接続を張る仕組みです。

通常、社内ネットワークは「外への通信は通すが、外からの直接アクセスは拒否」という設定になっていることが多いです。Cloudflare Quick Tunnels はこの前提に合わせて、ルーターで内向きの穴を開けずに外部と通信します。
外部サービス(SendGrid など)は Cloudflare が発行する URL(Quick Tunnels なら *.trycloudflare.com)に HTTPS で POST します。Cloudflare がそれを受け取り、すでに張ってあるトンネル経由でローカルの Web サーバーへリクエストを渡します。
ここでひとつ誤解されやすいのが、「外向き接続だけなら送信しかできないのでは?」という点です。実際には VM 側から Cloudflare へ接続を張り、その TCP 接続を維持したまま HTTP のリクエスト/レスポンスをやり取りします。接続の開始は「内→外」ですが、トンネルが確立されれば外部からローカルへ届く Webhook も受け取れます。
ここまでの説明が長くなってきたので、次は実際に接続できるか確認してみます。今回は、社内 LAN 上の Vagrant VM で Web サーバーを動かし、cloudflared で Quick Tunnels を張って、外部から HTTPS でアクセスできるかを見ます。
【今回の環境】
ホスト: macOS + Vagrant + VMware Fusion
ゲスト: Rocky Linux + Webmin + Virtualmin + LAMP
トンネル: cloudflared(Quick Tunnels)
また例によって Webmin / Virtualmin の設定等は割愛します(笑)。今回は EC-CUBE が表示できる環境にしています。

1)cloudflared をインストール
VM に SSH して、cloudflared をインストールします。

$ vagrant ssh
$ sudo su -
# curl -fsSL https://pkg.cloudflare.com/cloudflared.repo | tee /etc/yum.repos.d/cloudflared.repo
# dnf install -y cloudflared
# cloudflared --version
cloudflared version 2026.6.1 (built 2026-06-18-14:33 UTC)
バージョンが表示されれば OK です。
2)cloudflared を起動する(が失敗・・・)
それでは早速 cloudflared を起動してみます。

$ sudo su -
# cloudflared tunnel --url http://120.0.0.1:80
起動に失敗してコケています。原因を探ります。
failed to request quick Tunnel: Post "https://api.trycloudflare.com/tunnel":
dial tcp: lookup api.trycloudflare.com on 192.168.XXX.2:53: no such host
最後の方にメッセージが出ています。注目するのはメッセージの最後の部分です。
api.trycloudflare.com… cloudflared がトンネル作成時に接続する先192.168.XXX.2:53… 名前解決に使った DNS サーバー(ホストと同じ LAN 上の DNS )no such host… その DNS では名前を解決できなかった
DNSの名前解決のところで止まっている模様です。そのため、VM内でどのDNSを使用しているかを確認してみます。

# cat /etc/resolv.conf
nameserver がホストと同じ LAN 上の DNS (192.168.XXX.2)になっています。
続けて、外部ドメインが解決できるのかを確認してみます。

正常なら IP アドレスが返りますが、警告のメッセージが出ていて名前解決ができていません。


# ゲスト起動時に DNS を固定(192.168.XXX.2 対策)
# 注意: nmcli con down/up は SSH 経路を切るため使わない
config.vm.provision "shell", run: "always", inline: <<-SHELL
nmcli con mod "Wired connection 1" ipv4.dns "1.1.1.1 8.8.8.8"
nmcli con mod "Wired connection 1" ipv4.ignore-auto-dns yes
nmcli device reapply eth0
SHELL
3)Vagrant 再起動
Vagrantfile を修正したので、再起動してパブリック DNS が使用されるようにします。

最後の方のメッセージで、プロビジョニングのシェルが動いたことが確認できます。それでは先ほどと同じように、 VM に ssh で入って確認してみます。


パブリック DNS が使用されていることの確認と、名前解決ができて IP アドレスが取得できています。
4)cloudflared 起動(また別の問題・・・)
ここでもう一度、cloudflared を起動してみたのですがちょっと問題があったので、画像のキャプチャがないので記録で残します。
# cloudflared tunnel --url http://127.0.0.1:80
上記コマンドで cloudflared 自体は起動したのです。しかしながら、トンネルの確認を行うためにホストPC側から以下コマンドを実行したところ、400エラーとなってしまいました。
$ curl -I Cloudflare Quick Tunnelsから払い出されたURL
なんでかなと思い、よくよく思い出してみると今回は EC-CUBE を入れた環境です。
EC-CUBE の設定( .env )に TRUSTED_HOSTS 環境変数があり、ここで許可されていないと参照できないことを思い出しましたので、以下のように設定しました。

気を取り直して、今度こそと curl を 実行したら 今度は 301 リダイレクトがループするという状態になってしまいました。
これもなんでかなと考えてみると、
# cloudflared tunnel --url http://127.0.0.1:80
5)cloudflared 今度こそ起動
4)の経緯がありましたので、cloudflared を一度落として、再度以下コマンドで起動させます。
# cloudflared tunnel --url https://127.0.0.1:443 --no-tls-verify


無事起動しました・・・。

200 で 返ってきています。
では、インターネット越しでローカル環境のサイトが表示できるかを確認してみます。

表示されました!
6) Webhook の確認
今回自分が確認したかったのは、Webhook がローカル環境で受けられるかということでしたので、実験のために以下を準備しました。
webhook-test.php
<?php
// 検証用: トンネル経由の POST 確認
$logFile = __DIR__ . '/webhook-test.log';
file_put_contents(
$logFile,
date('c') . ' ' . file_get_contents('php://input') . "\n",
FILE_APPEND
);
header('Content-Type: application/json');
http_response_code(200);
echo json_encode(['ok' => true]), PHP_EOL;
中身は簡単なものですが、POSTされてきたら、内容をログファイルに書き込み、 response を返す処理です。ソースはドキュメントルートに設置して、ログはドキュメントルート直下の webhook-test.log に出力します。
ホスト PC を外部サービスと見做し、以下コマンドで POST します。
$ curl -i -X POST "https://【 Cloudflare Quick Tunnels で払い出されたURL】/webhook-test.php" -H "Content-Type: application/json" -d '[{"event":"test"}]'
【ホスト側】

【ゲスト側】


ローカル環境にPOSTされていることが確認できました!