Stripe · Deployment guide
Stripe webhook signature verification failed
Short answer
Stripe signs the raw request body. If your framework parses JSON before you verify the signature, every webhook fails. The other two usual suspects are wrong-mode secrets (test vs live) and auth middleware blocking Stripe's IPs.
Symptoms
- Logs: “No signatures found matching the expected signature for payload”.
- Webhook works with stripe-cli locally but fails in production.
- Some events verify, others don't (when only one endpoint is misconfigured).
- Stripe dashboard shows 401 / 403 on webhook attempts.
Common causes
- Handler calls <code>req.json()</code> or <code>express.json()</code> before computing the signature.
- STRIPE_WEBHOOK_SECRET is the test-mode secret but the endpoint is in live mode (or vice versa).
- Auth middleware (Clerk, Auth0, custom) blocks /api/webhooks/stripe.
- Body is gzip-encoded by a proxy/CDN before reaching your handler.
- Constructing the event with a stringified body instead of the raw Buffer.
How DeployDoc checks this
- Scans webhook handlers for <code>req.json()</code> / parsed-body bugs.
- Verifies STRIPE_WEBHOOK_SECRET prefix matches the endpoint mode (whsec_test_ vs whsec_).
- Flags webhook routes behind auth middleware allow-lists.
- Detects bodyParser middleware applied globally without an exception for /api/webhooks/*.
Fix it manually
- Read the raw body — Next.js App Router:
const body = await req.text(). - Construct the event:
stripe.webhooks.constructEvent(body, signature, secret). - Per environment: use the webhook secret from the matching dashboard mode.
- Add
/api/webhooks/*to your auth middleware's public allow-list. - In Pages Router, disable body parsing:
export const config = { api: { bodyParser: false } }.
When to run a DeployDoc diagnosis
Any time Stripe events stop being delivered after a deploy, a framework upgrade, or moving the webhook endpoint.