Overview
Verify a Stripe-style HMAC-SHA256 webhook signature locally with the browser's Web Crypto engine. Paste the raw payload, the signing secret and the signature header, press one button, and get a match or mismatch verdict. Useful for debugging webhook endpoints, understanding the signing scheme, and checking a captured request - without sending the secret or payload to any server.
How It Works
Paste the raw request body into the first field - exact bytes as your server received them, not a re-serialised version. Enter the signing secret (it begins with whsec_ for Stripe) and the signature header value in its t=timestamp,v1=hexhash form. Press Verify HMAC-SHA256. A valid signature is reported in green; a mismatch is reported in red with the expected hash's first sixteen hex characters so you can see how close you got. The page reminds you that verification always compares against raw payload bytes.
Step-by-Step Usage Guide
- Copy the exact raw payload your endpoint received.
- Enter the signing secret and the signature header (t=...,v1=...).
- Press Verify HMAC-SHA256 and read the verdict.
- On mismatch, check for payload re-serialisation before suspecting the secret.
Technical Specifications & Standards
The scheme implemented is Stripe's: the header carries a Unix timestamp t and one or more v1 signatures; the signed payload is the timestamp, a literal dot, and the raw request body concatenated; the signature is HMAC-SHA256 of that string keyed with the signing secret. The tool parses the header, imports the secret as a raw HMAC key through crypto.subtle, signs the reconstructed payload, hex-encodes the digest and compares it with the v1 value. Web Crypto is the right engine here because it is constant-time for the comparison-sensitive parts and never exposes the key material to page JavaScript beyond what you pasted. Two caveats are real: the tool checks the signature only, not timestamp freshness, so it does not detect replays - Stripe recommends rejecting events older than a few minutes, which your server must do separately. And any transformation of the payload (re-serialised JSON, added whitespace, charset changes) changes the bytes and breaks the match, which is the most common cause of a false mismatch during debugging.
Targeted Use Cases
- Debugging a webhook endpoint that rejects valid-looking events.
- Learning the Stripe signing scheme by recomputing a signature by hand.
- Checking a captured request from a proxy before replaying it safely.
- Verifying signature computation when migrating webhook handling between frameworks.
Notes & Gotchas
- Always paste the raw body exactly as received - reserialised JSON breaks the hash.
- Remember the tool verifies authenticity, not freshness; enforce a timestamp tolerance server-side.
- Use your endpoint's test-mode secret when experimenting; rotate anything you have pasted elsewhere.
- On mismatch, compare the first hex characters shown against your server's computation to localise the difference.
Frequently Asked Questions
What exactly is being signed?
The Unix timestamp from the header, a dot, and the raw request body, concatenated into one string, then HMAC-SHA256'd with your signing secret. That matches the Stripe scheme.
Does a valid signature prove the event is recent?
No. It proves the payload was signed by the secret and unmodified. Replay protection requires checking that the t timestamp is recent, which your server must enforce separately.
Why does a correct-looking setup still mismatch?
Almost always the payload bytes differ from what was signed - reserialised JSON, reordered keys, or a proxy that trimmed whitespace all change the hash input.
Is my secret sent anywhere?
No. The HMAC is computed with the browser's Web Crypto API locally. Nothing leaves the page.