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
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.
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());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.
- from $450App Fix TicketOne reproducible bug in an existing mobile or web app, diagnosed and fixed.See scope and start
- from $450AI-Built App RescueA Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.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.See scope and start