Webhook signature verifier

← All tools

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

Free books, courses, and software

Get my programming books, course editions, and complete software.

Get the download library →