Use cases
Stripe webhooks on localhost
Receive Stripe webhooks on your computer at a stable HTTPS URL, check the Stripe-Signature header, replay deliveries with a fresh signature, and keep events while you're offline.
Point a Stripe webhook endpoint at a handler running on your computer, watch each event
arrive in the inspector, and replay it while you fix the handler.
The examples use a handler at http://localhost:4242/webhook and the hostname
hooks.yourhost.com.
Or the Stripe CLI
stripe listen --forward-to localhost:4242/webhook forwards events to your computer
without a public URL, and is the quickest way to start. Use a Teitunnel address when you
want the real endpoint: its own signing secret, the event types you chose, Stripe's
retries and Event deliveries list, or events while the Stripe CLI isn't running.
1. Share the handler
teitunnel share 4242 --on hooks.yourhost.com --watch "/webhook"https://hooks.yourhost.com/webhook reaches your handler for as long as the share runs,
and it's the same address tomorrow. For an address that's always there, make it a
route instead.
2. Add the endpoint in Stripe
In a sandbox, open Workbench ▸ Webhooks ▸ Create an event destination, choose
Your account, the event types your handler needs, and Webhook endpoint. Enter
https://hooks.yourhost.com/webhook. On the endpoint's page, reveal the signing secret
(whsec_…) and give it to your handler.
Save the same secret in Teitunnel so the inspector checks every delivery's signature:
teitunnel inbox secret hooks.yourhost.com stripe # asks for the secretIn the app: select a delivery in the Inspector and choose Add Signing Secret….
3. Send an event
stripe trigger payment_intent.succeededThe share prints the delivery; the inspector shows it marked Webhook with
Signature valid, your handler's answer and how long it took. Stripe's Event
deliveries tab shows the same delivery as Delivered, Pending or Failed.
4. Fix and replay
Stripe signs a timestamp, and its libraries reject a signature older than 5 minutes. A
plain replay of an older delivery fails your handler's check, so turn on Re-sign in
Edit and Replay…: each replay gets a fresh Stripe-Signature with the saved secret.
teitunnel traffic ls --path /webhook --last 5
teitunnel traffic replay 7f3aThings Stripe is strict about
- The raw body. Verification needs the body exactly as sent. If your framework parses
JSON first (for example
express.json()), verify before parsing, or keep the raw bytes for the webhook route. - Answer quickly with 2xx, then do the work. A slow handler times out and is retried.
- Redirects fail. Stripe treats a
3xxas a failure: register the exact URL your handler answers, including a trailing slash if it wants one. - CSRF. Rails, Django and similar frameworks reject a POST without a CSRF token. Exempt the webhook route.
- Order and duplicates. Stripe doesn't promise order and may send an event twice. Record event ids you've handled.
- Test and live secrets differ. Each endpoint has its own secret per mode.
While your computer is off
In live mode Stripe retries a failed delivery for up to three days; in a sandbox only three times over a few hours. A webhook inbox keeps events that arrive while the computer is off and delivers them in order when it's back, keeping only correctly signed ones:
teitunnel inbox add hooks.yourhost.com /webhook --verify stripeA kept event reaches your handler later than 5 minutes, so its signature is too old for Stripe's default check. The inbox checked it on arrival; see Signatures and replays.
Checked 2026-09-26
From Stripe's webhooks documentation.
Related
Last updated on
Test webhooks on localhost
Receive Stripe, GitHub, Slack, Shopify or Twilio webhooks on your computer through Cloudflare Tunnel, at a stable HTTPS URL, then inspect, check and replay every delivery.
GitHub webhooks on localhost
Receive GitHub webhooks on your computer at a stable HTTPS URL, check X-Hub-Signature-256, replay deliveries, and keep them while you're offline, since GitHub doesn't retry.