Stripe / webhooks

Debug: "No signatures found matching the expected signature"

Stripe signs every webhook with HMAC-SHA256 over timestamp.payload using your endpoint's signing secret (whsec_...). If your handler rejects the event, it's almost always one of: wrong secret, a modified/re-serialized body (frameworks that auto-parse JSON before you verify), or comparing against the wrong signature scheme (v0 vs v1).

Privacy: this runs 100% in your browser with the Web Crypto API. Nothing you type is sent to any server — you can verify that by checking your browser's network tab while using it.

Check your signature

Most common causes, in order of likelihood

  1. Wrong secret. Each endpoint (and each of live/test mode) has its own whsec_.... Copy-pasting the secret from a different endpoint or from test mode into production is the single most common cause.
  2. Body already parsed as JSON. Express's express.json(), or any framework middleware that parses the body before your webhook route sees it, re-serializes the JSON — which changes key order or whitespace and breaks the byte-for-byte signature. You need the raw body, untouched, for the signed route specifically.
  3. Proxy or CDN rewriting the body. Some reverse proxies re-encode gzip or normalize line endings. Check the body length your handler receives against the length Stripe sent (visible in the Dashboard's webhook attempt log).
  4. Comparing against the wrong version. The header carries both v0 (legacy) and v1. Stripe's own libraries check v1; a hand-rolled check comparing against v0 will always fail.

If this pointed at the problem but you want someone to actually read your webhook handler code and confirm there's nothing else wrong — signature verification is rule #1 of 7 in our standalone checklist and in the full audit.

Get the 7-point checklist — $2

See all tools and the full audit ($39)