Stripe Error StripeSignatureVerificationError: Webhook signature verification failed
The HMAC of the body you passed does not match the Stripe-Signature header. Pass the raw, unparsed request body and the whsec_ secret of this exact endpoint.
Identify: does your log match?
StripeSignatureVerificationError: No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe?
If a webhook request is being forwarded by a third-party tool, ensure that the exact request body, including JSON formatting and new line style, is preserved.
Learn more about webhook signing and explore webhook integration examples for various frameworks at https://docs.stripe.com/webhooks/signature
at validateComputedSignature (/app/node_modules/stripe/cjs/Webhooks.js:149:19)
at Object.verifyHeaderAsync (/app/node_modules/stripe/cjs/Webhooks.js:98:20)- High
Body parsed as JSON before verify
express.json() or a framework re-serializes the body, changing whitespace and key order. The signature was computed over the original bytes.
- High
Wrong signing secret
Each endpoint, and each stripe listen session, has its own whsec_ secret. Test vs live and CLI vs Dashboard secrets differ.
- Low
Timestamp outside tolerance
The t= timestamp is older than the 300-second default tolerance, e.g. replayed events or a skewed server clock.
Fix
Use express.raw on the webhook route only
app.use(express.json()); app.post('/webhooks/stripe', (req, res) => { const event = stripe.webhooks.constructEvent( req.body, req.headers['stripe-signature'], process.env.STRIPE_WEBHOOK_SECRET); });
app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), (req, res) => { let event; try { event = stripe.webhooks.constructEvent( req.body, req.headers['stripe-signature'], process.env.STRIPE_WEBHOOK_SECRET); } catch (err) { return res.status(400).send(`Webhook Error: ${err.message}`); } res.json({ received: true }); }); app.use(express.json());
express.raw keeps req.body as a Buffer of the exact bytes Stripe signed. Register it before any global JSON parser.
Verify: reproduce it on purpose
$ curl -s -X POST http://localhost:3000/api/webhooks/stripe -H "Content-Type: application/json" -H "Stripe-Signature: t=1700000000,v1=deadbeef" -d '{"id":"evt_test","type":"payment_intent.succeeded"}'Run it against a dev environment to trigger StripeSignatureVerificationError on purpose and test your error handler.
Prevent
- Store whsec_ per environment; log only its last 4 chars.
- Return 2xx fast and process events in a queue.
- Make handlers idempotent on event.id; Stripe retries for up to 3 days.
FAQ
Why does verification pass with stripe listen but fail in production?
stripe listen prints its own whsec_ secret. Production must use the secret from the Dashboard endpoint (Developers > Webhooks > Signing secret).
Does a proxy or API gateway break the signature?
Yes, if it re-encodes, decompresses or pretty-prints the JSON body. Forward the body byte-for-byte.
Can I skip verification in development?
You can, but you then accept forged events. Use stripe listen --forward-to and its secret instead.
Related errors
Last verified 2026-10-23 · Source: Stripe OpenAPI spec + decline codes + webhook docs · Official docs ↗ · ~9,900 searches/mo