Free tool · Runs in your browser · No signup
Webhook signature verifier
Paste the raw body, the signature header and your signing secret. You get the digest the provider should have produced, the digest it actually sent, and the exact string the HMAC was computed over — which is usually where the mistake is.
What it checks
The four things that have to line up
The bytes
The signature covers the raw body, not the parsed object. Anything that re-serialises JSON before verification has already broken it, and the digest changes completely rather than slightly.
The signed string
Some providers sign the body alone, others prepend a timestamp with a period, a colon or a version marker. Get the separator wrong and everything else can be perfect.
The encoding
The same digest is hex for Stripe and GitHub, base64 for Shopify and Svix. Comparing a hex string to a base64 one never matches, no matter how correct the key is.
The key
Usually the secret as printed, but not always: some schemes want it base64-decoded to raw bytes first, and a test-mode secret will never verify a live-mode delivery.
9 schemes
What each provider signs
| Provider | Header | Scheme |
|---|---|---|
| Stripe | Stripe-Signature | HMAC-SHA256 over {timestamp}.{raw body}, hex, compared against each v1 in the header. |
| GitHub | X-Hub-Signature-256, X-Hub-Signature | HMAC-SHA256 over the raw body, hex, prefixed sha256=. |
| Shopify | X-Shopify-Hmac-Sha256 | HMAC-SHA256 over the raw body, base64. |
| Razorpay | X-Razorpay-Signature | HMAC-SHA256 over the raw body, hex. |
| Slack | X-Slack-Signature, X-Slack-Request-Timestamp | HMAC-SHA256 over v0:{timestamp}:{raw body}, hex, prefixed v0=. |
| Svix / Standard Webhooks | webhook-id, webhook-timestamp, webhook-signature | HMAC-SHA256 over {id}.{timestamp}.{raw body}, base64, key = base64-decoded secret. |
| Paddle (Billing) | Paddle-Signature | HMAC-SHA256 over {ts}:{raw body}, hex, read from the h1 field. |
| Twilio | X-Twilio-Signature | HMAC-SHA1 over the full URL plus, for form posts, every parameter sorted by name and concatenated as name+value. Base64. |
| Custom HMAC | whatever you configure | Configure the header, hash, encoding, and signed-payload template yourself. |
Discord and SendGrid are absent on purpose: they sign with a public key (Ed25519 and ECDSA P-256) rather than a shared secret, so there is no secret to paste and the check is a different operation. The signature guide covers both.
Answers
Questions about this tool
Is my signing secret sent to your servers?+
No. This page is a static file with no backend, and the HMAC is computed on your device with the browser’s WebCrypto API. The secret is held in a JavaScript variable for as long as the tab is open — it is never uploaded, never logged, and never written to localStorage, sessionStorage or a cookie. Reloading the page erases it.
The secret is definitely right, so why does the signature still not match?+
Almost always because the bytes being signed are not the bytes that were sent. Copying a payload out of a pretty-printed viewer adds indentation; a body-parsing middleware that parses and re-serialises JSON changes key order and spacing; and a trailing newline added by a shell or an editor is one more signed byte. Paste the raw body exactly as it arrived, with no reformatting.
Where do I get the raw body to paste in?+
From the sender’s delivery log — Stripe, GitHub, Shopify, Clerk, Paddle and Square all show the exact payload they sent, and most offer a copy button. If the provider keeps no log, point it at a TryWebhook URL, trigger the event again, and read the raw bytes from the capture.
Why would a correct signature be reported as outside the replay window?+
Stripe, Slack, Svix and Paddle sign a timestamp along with the body, and their own SDKs reject a request more than about five minutes old to prevent replay attacks. Anything pasted from a log is older than that, so this tool ignores the age by default. Untick “Ignore the timestamp age” to check the window as the provider would.
My provider is not in the list. Can I still check it?+
Choose “Custom HMAC” and describe the scheme: the header name, the hash, hex or base64, any prefix such as sha256=, and the signed-payload template. That covers most providers, because most of them are an HMAC over the body with or without a timestamp. It does not cover Discord or SendGrid, which use public-key signatures (Ed25519 and ECDSA) rather than a shared secret.
Can I verify a request I captured on TryWebhook?+
Yes, and it is easier to do it there: open the request in the inspector and use its Signature tab. The verification code is identical, but the inspector already has the exact bytes and every header, so there is nothing to paste and nothing to get wrong in the pasting.
Related
Go deeper
How to verify a webhook signature
The four decisions every scheme makes, a reference implementation, how to get the raw body in nine frameworks, and why the comparison must be constant-time.
Provider guides
The real headers, the real payload and the specific failure modes for Stripe, GitHub, Shopify, Razorpay and eleven others.
Skip the pasting
Capture the delivery instead.
Point the provider at a TryWebhook URL and the inspector verifies the signature against the bytes it actually received — nothing to copy, nothing to reformat by accident.
No signup · No email · Ready in about a second