~/tools / webhook-emulator
Webhook Cryptographic Header Emulator
Webhook handlers reject anything without a valid signature — which makes local testing
miserable without real provider secrets. Pick a provider, paste any secret and payload,
and get the exact signature headers computed the way Stripe, GitHub, or any HMAC-SHA256
service computes them. Native crypto.subtle only: your
secret never leaves this page.
— enter a secret to sign —
// curl command will appear here
$ man signature-schemes
- stripe — HMAC-SHA256 over
{timestamp}.{payload}, emitted ast={ts},v1={hex}. Timestamp is re-stamped on every edit. - github — HMAC-SHA256 over the raw body, emitted as
sha256={hex}. - generic — plain hex HMAC-SHA256 of the raw body in
x-signature.
Signatures are computed over the exact bytes in the textarea — whitespace included — matching how providers sign the raw request body.
cat ./tldr.txt
A webhook signature generator computes the cryptographic headers providers attach to their requests — Stripe stripe-signature, GitHub x-hub-signature-256, or generic HMAC-SHA256 — using the browser Web Crypto API, so signed requests can be sent to a local endpoint without provider credentials.
./when-to-use-this
- Your handler verifies signatures and rejects every hand-crafted test request you send it.
- You need to confirm your verification code rejects a tampered body as well as accepting a valid one.
- You want a ready-to-paste cURL command that will actually pass signature verification locally.
./frequently-asked
- How does Stripe signature verification actually work?
- Stripe computes HMAC-SHA256 over the string "{timestamp}.{raw_body}" using your signing secret, then sends it as t={timestamp},v1={hex}. Your handler must recompute the same value over the raw, unparsed body and compare in constant time. Verifying against a re-serialised body is the most common cause of false rejections.
- Is it safe to paste my real signing secret here?
- The secret is used only inside your browser via crypto.subtle and is never sent anywhere — there is no server, no logging, and no network request. It is not written to the URL either. That said, for shared machines or recorded screens, a local test secret is always the more cautious choice.
- Why does my signature not match even with the right secret?
- Almost always whitespace. Signatures are computed over exact bytes, so a body that has been pretty-printed, re-serialised, or had a trailing newline added produces a completely different hash. Sign and verify the identical raw string, and make sure your framework is not parsing the body before verification runs.