# Webhook をローカルで受けて試す方法：トンネル・各社の CLI・記録からの再送

> 手元の開発環境で Webhook を受けて試す 3 つの方法（ngrok・cloudflared のトンネル、Stripe CLI などの各社の CLI、記録した本文の送り直し）を、URL の固定・無料の範囲・署名の扱い・外に開く危険で比べます。

公開日: 2026-10-17
Source: https://webhookadmin.com/ja/blog/test-webhooks-locally/

## 要点

- トンネル（ngrok・cloudflared）は、実際の送信元から手元のサーバーまで本物の Webhook を通して試したいときに使います。
- 各社の CLI（stripe listen など）は、そのサービスの Webhook だけを、公開の URL なしで手元に転送したいときに使います。
- 記録した本文を curl や小さなスクリプトで送り直す方法は、同じ入力で何度も試したいときと、自動テストに向きます。

Webhook を手元の開発環境で受けて試す方法は、トンネルで手元のサーバーに公開の URL を付けて実際の送信元から送ってもらう方法、Stripe CLI のような各社の CLI でそのサービスの Webhook を手元に転送する方法、一度受けた本文を保存しておき署名し直して手元に送り直す方法の 3 つです。この記事では、3 つの向く場面と、署名付きで試すときの注意、手元を外に開く危険をまとめます。

## 3 つの方法の比較

| 方法 | 例 | 向く場面 | URL | 署名 |
|---|---|---|---|---|
| トンネル | ngrok、cloudflared | 本物の送信元から、手元までの経路を通して試す | 公開の https の URL が付く | 送信元の本物の鍵で署名される |
| 各社の CLI | `stripe listen` | そのサービスの Webhook を手早く試す | 公開の URL は不要 | CLI 用の鍵で署名される |
| 記録からの送り直し | curl、小さなスクリプト | 同じ入力で何度も試す、自動テスト | 不要 | 手元で署名し直す |

手元のサーバーを動かさずに、届いた中身を見るだけなら webhook.site のような道具もあります。こちらは[中身を見る道具の比較](https://webhookadmin.com/ja/blog/webhook-inspection-tools/)で扱います。

## 方法 1: トンネル

トンネルは、公開の URL に届いた要求を手元のポートへ転送する道具です。送る側の管理画面にその URL を登録すれば、本物の Webhook が手元のサーバーに届きます。

### ngrok

2026-09-27 に [ngrok の料金表](https://ngrok.com/pricing)と[無料の範囲の説明](https://ngrok.com/docs/pricing-limits/free-plan-limits)で確かめた無料の範囲は次のとおりです。

| 項目 | 無料の範囲 |
|---|---|
| 同時に開ける公開の入口 | 3 つまで |
| ドメイン | 開発用のドメインが 1 つ割り当てられる（`…ngrok-free.app`） |
| 転送量 | 月 1GB |
| HTTP の要求 | 月 20,000 回 |
| 警告のページ | ブラウザからの HTML の閲覧の前に出る。API やプログラムからの要求には影響しない |

割り当てられた開発用のドメインは使い続けられるので、送る側に一度登録すれば、トンネルを開き直しても URL を登録し直す必要はありません。有料の最初のプラン（Hobbyist）は月 10 ドルです。

```bash
ngrok http 3000
```

### cloudflared の Quick Tunnel

Cloudflare の Quick Tunnel は、アカウントを作らずに一時的な URL を発行できます（[Cloudflare のドキュメント](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/do-more-with-tunnels/trycloudflare/)、2026-09-27 に確認）。

```bash
cloudflared tunnel --url http://localhost:3000
```

起動するたびに `trycloudflare.com` のランダムなサブドメインが割り当てられるので、送る側の URL はその都度登録し直します。同時に処理中の要求が 200 を超えると 429 が返ること、Server-Sent Events を通さないことが書かれています。Cloudflare は Quick Tunnel を試験と開発のためのものとしています。

## 方法 2: 各社の CLI

Stripe の場合、Stripe CLI で `stripe listen` を動かすと、アカウントのイベントを手元の URL に転送できます。

```bash
stripe listen --forward-to localhost:3000/webhooks/stripe
# 別のターミナルで
stripe trigger payment_intent.succeeded
```

`stripe listen` は起動時に署名の鍵（`whsec_…`）を表示します。CLI で転送されるイベントはこの鍵で署名されるので、ダッシュボードで作った送信先の鍵とは別物です。Stripe の署名エラーの解説も、CLI の鍵とダッシュボードの鍵を取り違えないよう注意しています（[Stripe の Webhook](https://docs.stripe.com/webhooks)、[署名エラーの解説](https://docs.stripe.com/webhooks/signature)、2026-09-27 に確認）。手元の環境変数を CLI の鍵にし、本番では送信先の鍵にする、と分けておきます。

CLI は公開の URL が要らないので、トンネルを開けない社内のネットワークでも使えます。一方で、送信元から手元までの本当の経路（TLS、ロードバランサー、社外からの到達）は試せません。

## 方法 3: 記録した本文の送り直し

同じ入力で何度も試したいときは、一度受けた本文とヘッダーを保存しておき、手元のサーバーに送り直します。時刻を署名に含める方式では、保存した署名は許容幅（多くは 5 分）を過ぎると通らなくなるので、今の時刻で署名し直します。

Standard Webhooks の形式（Webhook Admin もこの形式）なら、npm の `webhookadmin` の `Webhook.sign` で署名を作れます。

```ts
import { readFileSync } from 'node:fs';
import { Webhook } from 'webhookadmin';

const secret = process.env.WEBHOOK_SECRET!; // 手元で試すための whsec_…
const body = readFileSync('saved-body.json', 'utf8'); // 受けたときの本文をそのまま
const id = 'msg_local_test_1';
const ts = Math.floor(Date.now() / 1000);

const signature = await new Webhook(secret).sign(id, ts, body);
const res = await fetch('http://localhost:3000/webhooks', {
  method: 'POST',
  headers: {
    'content-type': 'application/json',
    'webhook-id': id,
    'webhook-timestamp': String(ts),
    'webhook-signature': signature,
  },
  body,
});
console.log(res.status);
```

同じ `webhook-id` で 2 回送れば、受け側の重複の除去も試せます。この形は、そのまま受け側の自動テストにも使えます。自動テストの組み方は[受け側の自動テストの記事](https://webhookadmin.com/ja/blog/test-webhook-handlers/)で扱います。

## 署名付きで試すときの注意

- **トンネルを通しても本文は変わりません**。署名が合わないなら、原因は受け側のコードの本文の扱いか、鍵の取り違えです。切り分けは[署名検証が失敗する原因の記事](https://webhookadmin.com/ja/blog/webhook-signature-verification-failed/)にまとめました。
- **URL を署名に含める方式は、トンネルの URL で署名されます**。Twilio は、Twilio に設定した URL とパラメータで署名します。受け側が自分の見ている URL（`http://localhost:3000/…`）で検証すると合いません。登録したトンネルの URL で検証します。
- **鍵を分けます**。手元・テスト環境・本番で別の鍵を使い、環境変数の名前も分けておくと取り違えが減ります。

## 手元を外に開く危険と閉じ方

トンネルを開いている間、その URL を知っている人は誰でも手元のサーバーに要求を送れます。ngrok の開発用のドメインのように URL が固定される場合は、閉じたつもりでも、次に開いたときに以前の送り先から要求が届きます。

1. 手元でも署名の検証を外さない。検証を外した状態でトンネルを開かない。
2. Webhook の経路だけを受けるサーバーを立て、管理画面やデバッグ用の経路を同じポートに置かない。
3. 試験が終わったらトンネルのプロセスを止める。
4. 送る側に登録したトンネルの URL を消すか、送信先を止める。止めないと、送る側は失敗として再送を続けます。

## Webhook Admin から手元への試し送信

Webhook Admin には本番とテストの 2 つの環境があり、テスト環境の送信は月の通数に数えません。送信先の URL はテスト環境でも https が必要なので、手元で受けるときはトンネルの https の URL を登録します。

1. テスト環境の API キー（`sk_test_…`）で、トンネルの URL を送信先として登録する。
2. 送信先の試し送信（`POST /v1/endpoints/{id}/test`）で、その送信先だけに `webhook.test` を送る。試し送信は使用量に数えません。
3. 手元のサーバーで `webhookadmin` の `verify` を通す。
4. 失敗したら、管理画面の配信記録で応答のコードと応答の先頭 1,024 バイトを見て直し、記録から送り直す（`POST /v1/deliveries/{id}/retry`）。

Quick Tunnel のように URL が変わる場合は、[送信先の変更](https://webhookadmin.com/ja/docs/api/#update-endpoint)で URL だけを差し替えられます。記録からの送り直しでも `webhook-id` は同じなので、重複の除去もこの流れで試せます。鍵・送信先・データをテストと本番でどう分けるかは[テスト環境の記事](https://webhookadmin.com/ja/blog/webhook-test-environments/)で扱います。

送る側として手元の受信役に試し送信したい場合は、Webhook Admin を無料で登録できます（テスト環境の送信は数えません）。[https://app.webhookadmin.com/signup](https://app.webhookadmin.com/signup)

## よくある質問

### ngrok を使わずに Webhook を試せますか。

試せます。Cloudflare の Quick Tunnel（cloudflared tunnel --url）はアカウントなしで一時的な https の URL を発行できます。Stripe なら Stripe CLI の stripe listen で公開の URL なしに転送できます。記録した本文を手元で署名し直して送る方法なら、外部のサービスは要りません。

### 無料の ngrok で URL が変わるのが困ります。

2026-09-27 時点の ngrok の無料の範囲では、アカウントごとに開発用のドメインが 1 つ割り当てられ、同じドメインを使い続けられます。URL を固定したくない場合や、毎回新しい URL で構わない場合は cloudflared の Quick Tunnel も使えます。

### 受けた Webhook をもう一度送るにはどうすればよいですか。

送る側に再送の機能があればそれを使います（Stripe は CLI の stripe events resend、GitHub は管理画面と API の再配信）。手元で何度も試すなら、受けた本文を保存しておき、時刻を今にして署名し直してから curl などで送ります。
