Fix guides / Stripe: "No signatures found matching the expected signature"

Fixing Stripe's "No signatures found matching the expected signature for payload"

Payments go through, but your webhook handler throws Webhook signature verification failed: No signatures found matching the expected signature for payload. Stripe's check needs three things to be exactly right. One of them is not.

What the check needs

stripe.webhooks.constructEvent(body, signature, secret) needs: the raw request body exactly as Stripe sent it, the Stripe-Signature header, and the signing secret for that specific endpoint. If any one is off, you get this error.

Cause 1: the wrong signing secret (most common)

  • The secret printed by stripe listen on your laptop is different from the secret of the endpoint in your Stripe Dashboard. Production must use the Dashboard one.
  • Test mode and live mode endpoints have different secrets. If you just switched to live mode, create the live endpoint and copy its secret.
  • Find it in the Stripe Dashboard under Developers, then Webhooks (Workbench), then your endpoint, then Reveal secret. It starts with whsec_.
  • After changing the env var on your host, redeploy.

Cause 2: the body was parsed before you checked it

If your framework turns the body into JSON first, the bytes change and the signature no longer matches.

Next.js App Router: read the body as text.

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('Bad signature', { status: 400 });
  }
  // handle event.type ...
  return Response.json({ received: true });
}

Express: use the raw body for the webhook route, and register it before express.json().

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const event = stripe.webhooks.constructEvent(req.body, req.headers['stripe-signature'], secret);
  res.json({ received: true });
});
app.use(express.json()); // other routes after

Supabase Edge Functions (Deno): use the async version with the Web Crypto provider.

const cryptoProvider = Stripe.createSubtleCryptoProvider();
const body = await req.text();
const event = await stripe.webhooks.constructEventAsync(
  body, req.headers.get('Stripe-Signature'), Deno.env.get('STRIPE_WEBHOOK_SECRET'),
  undefined, cryptoProvider
);

Also deploy the function with JWT verification off (supabase functions deploy stripe-webhook --no-verify-jwt), because Stripe does not send a Supabase login token. Your signature check is what protects the endpoint.

How to verify

In the Stripe Dashboard, open your endpoint, find a failed event, and click Resend. A 200 response means it is fixed.

Still stuck? No shame in it: these bugs hide in settings your AI builder cannot see. Send us the error and your repo and we will fix it as a pull request within 24 hours, explained in plain English, for $49. Not fixed means full refund.
Request a fix

Last updated 2026-10-19.