How to Test Webhooks Locally

Webhooks need a public URL — but your code runs on localhost, which the internet can't reach. Here are three ways to bridge that gap and debug webhooks without deploying.

Why local webhook testing is hard

When a provider like Stripe or GitHub fires a webhook, its servers make an HTTP request to your endpoint. They need to reach it over the public internet. But http://localhost:3000 only exists on your machine — there's no route from the provider to it.

So before you can test a handler locally, you need a way for outside traffic to reach your machine, or a way to see the payload without exposing your machine at all.

Option 1: Capture the payload with a webhook tester

The fastest way to understand a webhook is to capture it. A webhook tester gives you an instant public URL; you paste that into the provider, fire an event, and inspect exactly what arrived — method, headers, body, signature.

  1. Generate a webhook URL.
  2. Paste it into the provider's dashboard as the webhook endpoint.
  3. Trigger a test event.
  4. Inspect the captured request — now you know the exact shape to code against.

This doesn't run your handler, but it removes all the guesswork: you write your receiver against a real, known payload instead of documentation.

Generate a capture URL →

Option 2: Tunnel traffic to localhost

A tunnel (ngrok, Cloudflare Tunnel, and similar) creates a public URL that forwards every request straight to a port on your machine. The provider hits the public URL; the tunnel pipes it to localhost:3000; your handler runs locally with a debugger attached.

Tunnels are powerful but have downsides: the public URL usually changes every restart (so you re-paste it into the provider each time), and you're exposing a live port on your machine for the duration.

Option 3: Capture, then forward to localhost

The most controlled approach combines the two: a stable capture URL receives every webhook (so the provider config never changes), and a small forwarder relays each captured request to your local handler. You get a permanent URL, a full history of every payload, and live local delivery — and you can replay any past request to re-test a fix without re-triggering the original event.

Replaying requests: the real productivity win

However you receive webhooks, the single biggest time-saver when debugging is replay. You capture one real event, then replay it against your handler as many times as you like while you fix the bug — no need to make another real payment or push another commit just to get another webhook.

Quick start: generate a URL, point your provider at it, fire one event, and inspect it. Once you know the payload, write your handler — then replay the captured event to test it.

Related guides