Error: No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe?
Node.js · updated 2026-11-11
Thrown by stripe.webhooks.constructEvent() (a StripeSignatureVerificationError, which logs as Error) in your webhook route when the stripe-signature header does not match the body you passed in. It also covers the wrong signing secret. Two related messages come from the same check: a parsed object instead of the raw body ("Webhook payload must be provided as a string or a Buffer"), and a missing header ("No stripe-signature header value was provided").
Error: No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? If a webhook request is being forwarded by a third-party tool, ensure that the exact request body, including JSON formatting and new line style, is preserved. Learn more about webhook signing and explore webhook integration examples for various frameworks at https://docs.stripe.com/webhooks/signature
Quick fix
Give constructEvent the exact bytes Stripe sent. In Express, mount express.raw({ type: 'application/json' }) on the webhook route, before any app-wide express.json(). In a Next.js route handler, read await request.text(). Then check the secret: each webhook endpoint (and the Stripe CLI) has its own whsec_ value, and test and live mode differ.
- Read which message you got: "No signatures found" means the body or the secret is wrong; "must be provided as a string or a Buffer" means a JSON parser ran first; "No stripe-signature header" means the header never reached your code.
- Express: use express.raw({ type: 'application/json' }) on the webhook route and register it before app.use(express.json()).
- Next.js App Router: const body = await request.text(); never request.json() before verifying.
- Use the signing secret of this exact endpoint and mode. stripe listen prints its own whsec_ for local testing.
- Check proxies and middleware that rewrite bodies (pretty-printing, re-serializing, gzip handling).
// Express: the webhook route gets the raw body; everything else can use JSON
app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), (req, res) => {
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(req.body, req.headers['stripe-signature']!, process.env.STRIPE_WEBHOOK_SECRET!);
} catch (err) {
return res.status(400).send(`Webhook signature check failed: ${(err as Error).message}`);
}
// handle event.type ...
res.json({ received: true });
});
app.use(express.json()); // after the webhook routePaste this into Claude Code, Cursor or Codex in the project that throws it. The agent reads this guide in plain text and applies the fix that matches your stack.
Read https://runheed.com/errors/stripe-no-signatures-found-matching-expected-signature.md and fix this error in this project.Why it happens
A JSON parser ran first
app.use(express.json()) or a framework body parser turned the body into an object; re-serializing it never matches the signed bytes.
Wrong signing secret
The secret belongs to another endpoint, to the Stripe CLI, or to the other mode (test vs live).
Body changed in transit
Middleware, a proxy or a framework adapter rewrote the body (formatting, encoding) before your handler saw it.
Header stripped
A proxy or the framework dropped the stripe-signature header, so constructEvent has nothing to check.
Other fixes
- Answer 400 on a failed check so Stripe retries later; don't answer 200 and drop the event.
- Report these failures to your error tracker with the endpoint path: a burst right after a deploy usually means a middleware order change.
Same error, other wording
- Webhook payload must be provided as a string or a Buffer instance representing the _raw_ request body. Payload was provided as a parsed JavaScript object instead.
- No stripe-signature header value was provided.
- stripe._error.SignatureVerificationError: No signatures found matching the expected signature for payload
Let your coding agent catch the next one
Runheed records this error in production with the stack trace, the breadcrumbs before it and, when you set it, the agent session that shipped the code, and serves it to Claude Code, Cursor or any MCP client. With it connected, you can ask:
> Find the Stripe webhook signature errors, check how the webhook route reads its body and which secret it uses, and fix the route so it verifies the raw body with the right endpoint secret.
Setup for Node.js and Express or Vercel Functions, then have your agent fix and resolve it.
Related errors
- Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
- SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
- TokenExpiredError: jwt expired
More in Network and HTTP
- Error: read ECONNRESETNode.js
- Error: connect ECONNREFUSED 127.0.0.1:5432Node.js
- Error: connect ETIMEDOUT 10.0.4.12:5432Node.js
- TypeError: fetch failedNode.js
- Error: self-signed certificate in certificate chainNode.js
- Error: getaddrinfo ENOTFOUND api.example.comNode.js
- Error: write EPIPENode.js
- Access to fetch at 'https://api.example.com/orders' from origin 'https://app.example.com'...Node.js
- AxiosError: Request failed with status code 401Node.js
- FunctionsHttpError: Edge Function returned a non-2xx status codeSupabase
- requests.exceptions.ConnectionError: HTTPSConnectionPool(host='api.example.com'...Python
- httpx.ReadTimeout: The read operation timed outPython
- ConnectionResetError: [Errno 104] Connection reset by peerPython
- BrokenPipeError: [Errno 32] Broken pipePython
- [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer...Python
- 422 Unprocessable Entity: {"detail":[{"type":"missing","loc":["body","email"],"msg":"Field...Python
- Error: 429 You exceeded your current quota, please check your plan and billing details.Node.js
References: Stripe docs: check webhook signatures, stripe-node source: webhook signature verification
Spotted a mistake? Email hello@greatwork.company. About Runheed.