What an HMAC is
An HMAC (hash-based message authentication code) is a hash of a message combined with a secret key. Only someone with the key can produce it, so a receiver who shares the key can check that a message is genuine and unchanged. Webhooks use it: the sender signs each request body and puts the HMAC in a header, and your server recomputes it to reject forged requests.
How to use the HMAC Generator
- Paste the message and your secret key, or load the GitHub example.
- Choose the algorithm and output format to read the HMAC.
- To check a webhook, choose the provider and paste its signature header (and, for Slack, the timestamp).
Examples
HMAC-SHA256 as hex
key: key
message: The quick brown fox jumps over the lazy dogf7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8The same as Base64
key: key
message: The quick brown fox jumps over the lazy dog97yD9DBThCSxMpjmqm+xQ+9NWaFJRhdZl0edvC0aPNg=Legacy HMAC-MD5
key: key
message: The quick brown fox jumps over the lazy dog80070713463e7749b90c2dc24911e275Some older APIs still use HMAC-MD5. Unlike plain MD5, HMAC-MD5 hasn't been broken in practice, but prefer SHA-256 for anything new.
How each provider signs
- GitHub:
X-Hub-Signature-256: sha256=<hex>, an HMAC-SHA256 of the raw body. - Stripe:
Stripe-Signature: t=<timestamp>,v1=<hex>, an HMAC-SHA256 of<timestamp>.<body>. - Slack:
X-Slack-Signature: v0=<hex>, an HMAC-SHA256 ofv0:<timestamp>:<body>, with the timestamp fromX-Slack-Request-Timestamp. - Shopify:
X-Shopify-Hmac-Sha256: <Base64>, an HMAC-SHA256 of the raw body.
Why verification fails
Almost always because the body isn't the exact bytes that were signed. Frameworks often parse JSON and hand you an object; serialising it again changes spacing and key order. Verify against the raw request body, before parsing. Also check you're using the right secret for the environment (test or live).
Options
- Algorithm
- SHA-256 (the default and the most common), SHA-1, SHA-384, SHA-512 or MD5.
- Key format
- The key as text (UTF-8), hex or Base64. Some providers give secrets in Base64 that must be decoded first.
- Output
- Hex, Base64 or URL-safe Base64, whichever the receiving system expects.
Limitations
- The message is signed as UTF-8 text. For binary payloads, use your server's code.
- Webhook verification checks the signature only; your server should also reject old timestamps and repeated deliveries.
Frequently asked questions
Is my secret sent anywhere?
No. The HMAC is calculated in your browser with the Web Crypto API; the key and message never leave the page.
Why doesn't my signature match?
Check that you're signing the raw body exactly as received, using the right secret (test and live secrets differ) and the right key format. For Stripe and Slack, the timestamp is part of what's signed.
How do I compute an HMAC in code?
Node.js: createHmac("sha256", secret).update(body).digest("hex"). Browsers: crypto.subtle.importKey then crypto.subtle.sign("HMAC", key, data). Python: hmac.new(secret, body, hashlib.sha256).hexdigest(). Compare signatures with a constant-time function such as crypto.timingSafeEqual.