How to test Stripe webhooks locally (and what it won't prove)

You can't point Stripe at localhost — it's not reachable from the internet. Testing a webhook handler locally means bringing the events to your machine instead, and the Stripe CLI does exactly that. The part worth getting right is what a passing local test does and doesn't prove.
The Stripe CLI, in two commands
The official way needs no tunnels or ngrok. Two commands cover it:
stripe listen --forward-to localhost:PORT/api/webhooklogs the CLI into your account and forwards real events straight to your local route. On start, it prints a signing secret (awhsec_value) — use that as your webhook secret while testing.stripe trigger checkout.session.completed(or any event name) fires a realistic test event on demand, so you can exercise the handler without making test purchases by hand.
stripe listen is specific to the CLI session— it is not the same secret as your dashboard endpoint. Wire your local env to the CLI secret, your deployed env to the dashboard secret, and never cross them. A mismatch here is the single most common "works locally, 400s in production" cause.This prompt sets the whole thing up for your framework:
Set me up to test my Stripe webhook handler locally with the Stripe CLI. I'm using <FRAMEWORK> and my local server runs on port <PORT>. Requirements: - Show me the stripe listen command that forwards events to my local webhook route, and explain that it prints a CLI-specific signing secret (whsec_...) I must use as my webhook secret WHILE testing locally — it's different from my dashboard endpoint's secret. - Show me how to fire specific test events with stripe trigger (e.g. checkout.session.completed, invoice.paid) so I don't have to make real test purchases. - Remind me that my handler must still read the RAW body for signature verification even locally, and that local success does not prove production works, because production uses a different signing secret and my real body-parsing middleware. - Give me a quick checklist to verify before I rely on it in production. Give me the exact commands and the local env var setup.
Stripe's local testing docs cover the CLI commands and event list.
Why local success can still lie to you
Here's the trap the tutorials skip: a webhook that passes locally can still fail the moment it's deployed. The two environments differ in exactly the ways that break webhooks.
Production uses a different signing secret (your dashboard endpoint's, not the CLI's). It runs behind your real body-parsing middleware, which may mangle the raw body the CLI's forwarding didn't — the classic signature-verification failure. And it's subject to real deploys, timeouts, and cold starts a local run never sees.
So local testing proves your logic is right. It says almost nothing about whether the live endpoint actually works — which is a separate thing you have to confirm after every deploy.
Testing is a moment; production is forever
You test a webhook once and then trust it for months, during which a deploy renames a route, a dependency changes how the body is parsed, or an endpoint gets auto-disabled after three days of failures. None of that shows up in a local test you ran in March.
The counterpart to testing locally is watching the live endpoint continuously. Tell Me When Down does that — so the webhook you verified in development stays verified in production, and you hear about it the day it stops.
Test it once locally. Watch it forever in production.
Join Tell Me When Down free and we'll watch your live Stripe webhook endpoint around the clock. A deploy that breaks the route, a body parser that changed, an endpoint Stripe disabled — you get an email in minutes, not a customer complaint.
spot something wrong or out of date? [email protected] — we'll fix it