Skip to content

Stripe webhook errorNo signatures found matching the expected signature for payload

Stripe rejected the webhook because one of three things didn’t match: the raw request body, the Stripe-Signature header or the endpoint’s signing secret. Here’s how to find which one, or hand it over at a fixed price.

Short answer

Usually it’s the wrong signing secret. Each endpoint has its own, the Stripe CLI prints a different one, and the same endpoint has a different secret for test and live keys. Next most likely: your framework parsed or changed the request body before the signature check ran.

The three causes, and the fix

  1. 01

    The wrong signing secret

    Copy the whsec_ secret from the endpoint Stripe is actually sending to. Testing locally with stripe listen? Use the secret the CLI prints, not the Dashboard’s. Using one endpoint for test and live? Each mode has its own secret, so the deployed app needs the live one.

  2. 02

    The body changed before verification

    Stripe signs the raw request body, byte for byte. If your framework parses it as JSON, reorders keys, trims whitespace or changes the encoding first, the signature can’t match. In Express, give the webhook route the raw body and register express.json() after it.

    app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
      const event = stripe.webhooks.constructEvent(
        req.body, req.headers['stripe-signature'], endpointSecret);
      res.sendStatus(200);
    });
    app.use(express.json());
  3. 03

    The header didn’t arrive as sent

    Check that the Stripe-Signature header reaches your code unchanged, in its t=...,v1=... form. Anything in front of your app that drops or rewrites headers can break it. The header also carries a timestamp, and Stripe’s libraries reject events older than 5 minutes by default, so check your server clock if every event fails.

Checked against the vendor’s docs on : Stripe docs: Receive Stripe events in your webhook endpoint; Stripe docs: Resolve webhook signature verification errors.

Rather hand it over?

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

Related: Stripe payment went through, account not upgraded

Questions

More on this error

Is the signing secret the same as my API key?

No. The webhook signing secret starts with whsec_ and belongs to one endpoint. Your secret API key starts with sk_ and is what your server uses to call Stripe. Verifying a webhook with the API key always fails.

It works locally and fails in production. Why?

Locally, the Stripe CLI forwards events signed with its own secret. In production, Stripe signs with the secret of the Dashboard endpoint, and live mode has a different secret from test mode. Set the production secret from the live endpoint, then resend a failed event from the Dashboard to confirm.

Can I just skip the signature check?

Don’t. Without it, anyone who finds your webhook URL can send a fake "payment succeeded" event and get access for free. Fix the secret or the raw body instead.

Can you fix it for me?

If one bug is the whole problem, the App Fix Ticket covers it: a written diagnosis first, then the fix. If the app has more going on, the Launch Check is a written check of the whole app. Each issue found is rated critical, high, medium or low, with where it is and how it was confirmed. No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way.

A different error?

Describe it and get a written quote

Send the error and what you were doing. You get a written scope, a fixed price and a timeline; we reply by email, usually within one business day.