Webhook signature verifier
Paste a signature header, the raw body and the signing secret. This tool reproduces the provider's HMAC, compares it in constant time and tells you which check failed — with working code for five schemes.
Last reviewed · five schemes · no account needed
A webhook signature is the one part of a delivery that proves the body was signed with a secret the provider gave you. When it fails in production it is rarely the cryptography: it is the wrong credential, a body that was parsed and re-serialised, an encoding mismatch, or a timestamp that fell outside the provider's window. This tool exists to make that failure legible in one place, without deploying a debug handler.
Each scheme gets its own page because the details differ in ways that matter. Stripe combines a timestamp and the body with a dot; Shopify and LINE sign the body with a base64 digest; GitHub sends a hex digest with a `sha256=` prefix; Slack signs a string that includes a separate timestamp header and rejects requests older than five minutes. A checker that treats these as one shape quietly produces wrong answers for four of them.
Everything runs on AnyHook's own API route: one HMAC per check, no database, no third-party call, and no echo of the secret. Results show the expected and provided signatures masked so two values can be compared by eye while neither is a usable credential. The code samples on each page are the production version of the same check.
The five schemes at a glance
The differences are not cosmetic: the signed string, the encoding and the presence of a timestamp change the code you write. The table is the provider table this site publishes, restricted to the five checkers here.
| Provider | Signature header | Signed string | Encoding | Tolerance |
|---|---|---|---|---|
| Stripe | stripe-signature = t=X,v1=hex | {timestamp}.{body} | hex | 5 min |
| Shopify | x-shopify-hmac-sha256 | raw body | base64 | Not published |
| GitHub | x-hub-signature-256 = sha256=hex | raw body | hex | Not published |
| Slack | x-slack-signature = v0=hex | v0:{timestamp}:{body} | hex | 5 min |
| LINE | x-line-signature = base64 | raw body | base64 | Not published |
Open a checker
Stripe
stripe-signature
Paste a Stripe-Signature header, the raw request body and the endpoint's signing secret to see whether the HMAC matches — and read the exact scheme Stripe uses.
Open the Stripe checker →Shopify
x-shopify-hmac-sha256
Shopify signs each webhook with a base64 HMAC-SHA256 of the raw body. Paste the X-Shopify-Hmac-Sha256 header, the body and your app's client secret to check it.
Open the Shopify checker →GitHub
x-hub-signature-256
GitHub sends `sha256=` plus a hex HMAC-SHA256 of the raw body in X-Hub-Signature-256. Paste that header, the body and the webhook secret to check it.
Open the GitHub checker →Slack
x-slack-signature + x-slack-request-timestamp
Slack signs requests with v0= plus an HMAC-SHA256 over the version, the request timestamp and the raw body. Paste all three into this checker.
Open the Slack checker →LINE
x-line-signature
LINE signs its webhook with a base64 HMAC-SHA256 of the raw body in X-Line-Signature. Paste the header, body and channel secret to check it.
Open the LINE checker →Handler too slow?
A signature can be valid while the sender has already hung up. Time your handler against each sender's response budget with the free timeout checker.
Open the webhook timeout checker →How to use it
- 1
Pick the provider whose webhook you are debugging. The page for it states the exact string that is signed and the header that carries the signature, alongside a sample you can copy.
- 2
Paste the signature header as JSON, the raw request body exactly as it arrived, and the signing secret. If you do not have a real credential to hand, press “Load a valid sample” to generate a throwaway secret and a matching signature, then verify it.
- 3
Read the reason, not just the verdict. A missing header, a malformed prefix and a wrong secret fail differently, and the result names which one happened. The failures table on the page maps each symptom to its fix; the code samples are what you keep in the handler.
What this tool cannot tell you
- It cannot prove the sender was the provider; only that the body matches the secret you supplied.
- It cannot replay-protect a scheme that has no timestamp, and it does not enforce Stripe's default age unless asked to.
- It cannot fix a body that was already parsed or rewritten; paste the raw bytes.
- It cannot validate the event's business meaning or whether you have processed it before.
Frequently asked
- Is this the same check my server runs?
- It is the same arithmetic. The tool reproduces the provider's HMAC over the header(s) and body you paste and compares in constant time. Your server still has to read the raw body itself; the code samples show that part with no framework magic.
- Does it store my secret or payload?
- No. The page posts the values to AnyHook's own API route, which computes one HMAC per request. Nothing is written to a database, nothing is logged, and the result only contains masked signatures. That said, a secret is still a secret: use a test credential when you are unsure.
- Which five schemes are covered?
- Stripe, Shopify, GitHub, Slack and LINE. They were chosen because each has a different signed string or encoding, and because they are common enough that a copy-paste mistake is likely to be diagnosed here first.
- Why do some checks ignore the timestamp?
- Only schemes that define a timestamp can check one. Stripe and Slack include a timestamp; the others do not, and the page says so rather than inventing a tolerance. For Stripe the tool checks the HMAC without rejecting old requests, because captured requests are what people bring here; enforce Stripe's five-minute window in production.
- Can it tell me whether the request came from the provider?
- No. A matching HMAC proves the body was signed with the secret, not that the sender's IP or TLS connection belonged to the provider. IP allow-lists are a separate control, and this page never claims them.
Signatures checked, delivery still failing?
A valid signature only says the body was signed. If your handler answers too slowly, the sender records a failure and retries — or disables the endpoint. AnyHook keeps the event, retries it and lets you replay it; start with the free timeout checker to see which senders would hang up on your endpoint.