Webhook signature verifier
Paste a webhook's raw body, its signature header and your signing secret. The tool recomputes the HMAC the way the provider does, tells you if it matches, and when it doesn't, checks the usual reasons: a reformatted body, a stray newline, the wrong secret.
Everything runs in your browser with Web Crypto. The secret and body are never sent anywhere and never stored in the URL. Prefer a test-mode secret anyway.
Provider
Paste the header value. A leading "Header-Name:" is ignored. Leave it empty to generate the header for a test request.
Paste the body exactly as it arrived. A reformatted copy or one extra newline gives a different signature.
Fill in the secret, the header and the body, or load an example.
Can't check this yet
✓ Signature matches
This body was signed with this secret.
✗ Signature does not match
No signature to compare
Here's the header a sender would produce for this secret and body. Use it to send a test request to your handler.
Notes
Expected header value
Received signatures
Signed content
About this tool
Most webhook providers sign each request with an HMAC: they hash the body (sometimes with a timestamp and an id in front) using a secret you share with them, and put the result in a header. Your handler does the same computation and compares. If someone forges or edits the request, the two don't match.
Each provider builds the signed content its own way. Stripe signstimestamp.body and sends t=…,v1=…. GitHub signs the body alone and prefixes sha256=. Slack signsv0:timestamp:body. Shopify sends a base64 digest of the body. Paddle Billing signs ts:body. Standard Webhooks signsid.timestamp.body with a key decoded from the base64 part of a whsec_ secret. This tool follows each of those schemes as written in the providers' docs. Custom HMAC covers services that sign the raw body with a plain secret.
The number one reason a valid webhook fails verification is the body. Frameworks often parse JSON before your code sees it, andJSON.stringify of the parsed object is not the same string that was signed. Verify the raw bytes first, then parse. When the signature doesn't match, this tool retries with a minified and a pretty-printed copy of the JSON, with and without a trailing newline, with CRLF line endings, and with a trimmed secret, and tells you which one matched.
Providers with a timestamp also reject old requests to stop replays. The tool shows how old the signature is next to each provider's default window, so a valid but old test payload is easy to tell apart from a broken one. In your own code, compare signatures with a constant-time function like crypto.timingSafeEqual.
Read more
- What is a webhook?
- How to get both parsed body and raw body in Express — the fix for most verification failures
- The complete guide to Cloudflare Quick Tunnels — receive webhooks on localhost
- How JWT signing actually works (HS256 with Web Crypto) — the same HMAC primitive
Free books, courses, and software
Get my programming books, course editions, and complete software.
Get the download library →