← All errors · StripeStripe payments & webhook errors
No signatures found matching the expected signature for payload
Exact error
StripeSignatureVerificationError: No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe?
What it means
stripe.webhooks.constructEvent() could not verify that the webhook came from Stripe. Verification needs the exact raw body bytes and the matching endpoint signing secret.
Likely causes
- The body was parsed as JSON (or re-serialized) before verification.
- Using the wrong signing secret: each endpoint, and the Stripe CLI, has its own whsec_ secret.
- Test-mode secret used for live-mode events or vice versa.
How to fix it
In a Next.js App Router route handler, read the raw text body:
import Stripe from 'stripe'; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY); export async function POST(req) { 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 (err) { return new Response(`Webhook Error: ${err.message}`, { status: 400 }); } // handle event.type ... return new Response('ok'); }In Express, use express.raw for the webhook route only:
app.post('/webhook', express.raw({ type: 'application/json' }), handler);When testing locally with `stripe listen`, use the whsec_ secret the CLI prints, not the dashboard one.
Prevent this next time
- Test webhooks locally with `stripe listen` and the Stripe test cards before going live.
- Check webhook delivery attempts in the Stripe Dashboard after each deploy, and keep test and live keys clearly separated per environment.
FAQ
What does "No signatures found matching the expected signature for payload" mean?
stripe.webhooks.constructEvent() could not verify that the webhook came from Stripe. Verification needs the exact raw body bytes and the matching endpoint signing secret.
What usually causes it?
The body was parsed as JSON (or re-serialized) before verification. Using the wrong signing secret: each endpoint, and the Stripe CLI, has its own whsec_ secret. Test-mode secret used for live-mode events or vice versa.
How do I fix it?
1. In a Next.js App Router route handler, read the raw text body: 2. In Express, use express.raw for the webhook route only: 3. When testing locally with `stripe listen`, use the whsec_ secret the CLI prints, not the dashboard one.
Related errors
Get new fixes weekly
No spam. Unsubscribe anytime. Privacy