Webhook Signature Verifier — HMAC Checker
Check whether a webhook payload really came from Stripe, GitHub, Shopify or Slack by recomputing its HMAC signature with your signing secret. The digest is computed by your browser's own Web Crypto engine, so the secret never leaves the page.
Expected signature
Signed string
This is the exact string the provider signs. When a signature will not verify, this is usually where the mismatch is — not the secret.
What Webhook Signature Verifier does
Your signing secret stays local
The digest is computed by crypto.subtle in your own browser. A webhook signing secret is a long-lived credential for your whole endpoint — pasting one into a server-side tool is a genuine risk, not a theoretical one.
Shows the string that gets signed
Stripe signs {timestamp}.{body} and Slack signs v0:{timestamp}:{body} — not the bare payload. Seeing the exact signed string is usually what explains a mismatch.
Replay window checked
For providers that sign a timestamp, a payload older than the documented tolerance is flagged even when the digest matches — because a real endpoint would reject it.
Presets that match the docs
Each provider's header name, digest algorithm, encoding and prefix are built in, so you are not re-reading four sets of documentation to find out whether it is hex or base64.
Supported formats
The tool reads raw text, so whatever your stack produces will work.
How it works
Pick the provider
Choosing Stripe, GitHub, Shopify or Slack sets the header name, algorithm and signed-string format for you. Two generic presets cover everything else.
Paste the secret and the raw body
Use the body exactly as received — byte for byte. Re-indenting the JSON changes the bytes and the signature will not match.
Paste the signature header
The computed digest is compared against the one you were sent, and the verdict appears immediately.
Cheatsheet
The tokens you reach for most, at a glance.
StripeStripe-Signature: t=…,v1=… — signs {timestamp}.{body}, hexGitHubX-Hub-Signature-256: sha256=… — signs the raw body, hexShopifyX-Shopify-Hmac-Sha256 — signs the raw body, bare base64SlackX-Slack-Signature: v0=… — signs v0:{timestamp}:{body}, hexToleranceStripe and Slack both reject payloads older than 5 minutesRaw bodySign the bytes received, never a re-serialised objectHow people use it
Your endpoint rejects a live webhook
Confirm whether the signature is genuinely wrong or your handler is signing the wrong string — the two failures look identical in a log.
Testing a handler locally
Generate a valid digest for a body you control and use it to exercise your verification code before going near production.
Confirming a suspicious payload
An unsigned or wrongly-signed request to a public webhook URL is worth knowing about. Verification is what separates a real event from a forged one.
Frequently asked questions
Is my signing secret sent anywhere?
No. The HMAC is computed by your browser's built-in Web Crypto engine (crypto.subtle) on this page. There is no request and no log. The secret is not stored either — reloading clears it.
The digest does not match. What is usually wrong?
Almost always the body. Frameworks that parse JSON before your handler runs give you a re-serialised object, not the bytes that were signed — a single changed space breaks the digest. Capture the raw body and compare it against the signed string shown here.
Why does Stripe include a timestamp?
To stop replay attacks. Because the timestamp is part of the signed string, an attacker cannot resend a captured payload later without invalidating the signature. Stripe rejects anything more than five minutes old, and this tool flags it.
Why compare digests in constant time?
A plain === returns as soon as two strings differ, and the timing difference can leak the correct signature one character at a time. The comparison here — and the one in your handler — should examine every character regardless.
Which algorithms are supported?
HMAC with SHA-256 and SHA-1, in hex or base64. SHA-1 is included only because some older providers still send it; do not choose it for a new integration.
Can I verify a provider that is not listed?
Use one of the generic presets if the provider signs the raw body with HMAC-SHA256 or SHA-1. If it folds a timestamp or a URL into the signed string, the digest here will not match — check its documentation for the exact format.
Last updated 2026-08-22