00 / guide
Stripe webhook signature verification failed in Next.js
Your handler throws No signatures found matching the expected signature for payload. Stripe says at least one of three inputs is wrong: the body, the header, or the secret. Stripe: resolve signature errors
Free tool: check your Stripe-Signature in the browser
01 / check these three
1
The raw body
Stripe signs the exact text it sent. If anything parses or changes the body first, the check fails. Stripe: check the request body
App Router (app/api/.../route.ts): read the body with await req.text() and pass that string straight in. Don't call req.json() first, and don't rebuild the text with JSON.stringify.
export async function POST(req: Request) {
const body = await req.text()
const sig = req.headers.get("stripe-signature") ?? ""
let event
try {
event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!)
} catch {
return new Response("bad signature", { status: 400 })
}
// handle event, then answer 2xx quickly
return Response.json({ received: true })
}Pages Router (pages/api/...): turn off the built-in body parser and read the raw buffer, as Stripe's docs say. Stripe: framework table
Stripe's working Next.js example: stripe-node webhook-signing/nextjs
2
The endpoint secret
The most common cause is the wrong secret. The secret from stripe listen is different from the one for an endpoint you made in Workbench. Both start with whsec_. Stripe: check the endpoint secret
Sandbox and live endpoints have different secrets too. Make sure each deployment (preview, production) has the secret for the endpoint that points at it.
To compare, look at the last 4 characters. Don't paste a whole secret into logs or chat.
3
The Stripe-Signature header
Pass the header value as it came. It looks like t=…,v1=…. If yours doesn't, check how you read the header. Stripe: check the signature header
02 / after the fix
a
Test it
Resend a failed event from Workbench (Webhooks, your endpoint, Event deliveries), or run stripe trigger checkout.session.completed while stripe listen forwards to your local app. A good delivery shows 200. Stripe: verify events
03 / where CheckoutAlarm fits
When your handler answers 400, Stripe counts the event as not delivered. With a read-only Stripe key, CheckoutAlarm emails you when events are still not delivered after about 15 minutes. It can't read your logs, so it won't tell you which of the three inputs is wrong.
CheckoutAlarm is new. It only checks sites you prove you own. How to verify
Watch my app free04 / sources
- Stripe: manage webhook endpoints (resolve signature verification errors)
- Stripe: receive events, verify events
- Stripe: Next.js webhook-signing example (stripe-node)
Docs checked on Oct 18, 2026.