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.

ProviderSignature headerSigned stringEncodingTolerance
Stripestripe-signature = t=X,v1=hex{timestamp}.{body}hex5 min
Shopifyx-shopify-hmac-sha256raw bodybase64Not published
GitHubx-hub-signature-256 = sha256=hexraw bodyhexNot published
Slackx-slack-signature = v0=hexv0:{timestamp}:{body}hex5 min
LINEx-line-signature = base64raw bodybase64Not published

Open a checker

How to use it

  1. 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. 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. 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.