How to Debug Stripe Webhooks
Stripe webhooks are how your application learns about payments, subscription changes, disputes, and dozens of other events. When they work, they are invisible. When they break, you get silent failures — customers pay but do not get access, subscriptions expire without notice, refunds go unrecorded.
Here are the most common Stripe webhook issues and how to diagnose them.
1. Signature Verification Failures
Every Stripe webhook includes a Stripe-Signature header. Your handler should verify this signature using your webhook signing secret to confirm the request actually came from Stripe.
The most common causes of signature verification failures:
- Wrong signing secret. Stripe gives you a different signing secret for each webhook endpoint. If you have multiple endpoints (one for test mode, one for live mode), mixing them up will cause every verification to fail. Check the Developers > Webhooks page in your Stripe dashboard.
- Body parsing modifies the payload. Signature verification requires the raw request body. If your framework parses the JSON before you verify the signature, the raw bytes will not match. In Express, use
express.raw({type: 'application/json'})for your webhook route. In Next.js App Router, read the body withawait request.text()before parsing. - Proxy or middleware modifying headers. If a reverse proxy strips or rewrites headers, the signature header may arrive altered. Check that
Stripe-Signaturepasses through unchanged.
2. Missing or Unexpected Event Types
Stripe sends many event types, and your endpoint receives all the ones you subscribed to. A common mistake is subscribing to checkout.session.completed but not invoice.payment_succeeded, then wondering why recurring payments are not updating your database. For subscriptions, you typically need to handle both.
Another issue: your handler throws an error on event types it does not recognize. Always include a default case that returns 200 for unhandled event types. If you return a 4xx or 5xx for an event you do not care about, Stripe will keep retrying it.
3. Retry Storms and Duplicate Events
Stripe retries failed webhook deliveries for up to 72 hours with exponential backoff. If your handler is down or returning errors, events queue up. When the handler comes back, you get hit with a burst of retries.
To handle this safely:
- Make handlers idempotent. Use the event ID (
evt_...) to check if you have already processed this event. Store processed event IDs and skip duplicates. - Return 200 quickly. Do the minimum work in the webhook handler itself. Queue heavy operations (sending emails, provisioning accounts) for async processing. If your handler takes more than 20 seconds, Stripe considers it timed out.
4. Test Mode vs. Live Mode Confusion
Stripe has completely separate webhook configurations for test and live mode. Events from test mode will not trigger your live webhook endpoint, and vice versa. If you are testing a payment flow in test mode but your webhook URL is registered under live mode, nothing will arrive.
Check the mode toggle in your Stripe dashboard when debugging missing events. This is one of the most common "it works in production but not in development" issues.
5. Inspect Before You Code
Before writing or debugging handler code, it helps to see exactly what Stripe sends. The Stripe dashboard has an event log, but it shows you a processed view of the event — not the raw HTTP request your server receives.
HookTest lets you create a public URL in one click and point your Stripe webhook at it. You will see the full request: headers (including the Stripe-Signature), body, query parameters, and method. This is useful for verifying that your body parsing does not mangle the payload, checking which headers arrive, and understanding the exact structure of event payloads before you write a line of handler code.
Once your handler is ready, you can configure the bin to auto-forward events to your local server, so you do not have to change the webhook URL in Stripe's dashboard during development.
Further Reading
New to webhooks? What Is a Webhook? A Plain-English Explanation covers the fundamentals. If you are looking for ways to test webhooks on localhost, How to Test Webhooks Locally Without ngrok walks through your options.