
Enter a secret key and a message, and this generator shows the HMAC signature as you type. It supports HMAC-SHA256, SHA-512, SHA-384, SHA-1, and MD5, with output in hex, base64, or base64url. Presets for GitHub, Stripe, Slack, and Shopify build the exact string each service signs and show the header value you should see, so you can debug webhook signature checks. A JWT mode signs HS256, HS384, and HS512 tokens. Paste an expected signature to check whether it matches. Your key never leaves your browser.
How to generate an HMAC signature
- Enter the secret key. Choose Hex or Base64 if your key is stored that way, so its bytes are read correctly. Click Random to create a new 32-byte key.
- Paste the message, or click File to sign a file's exact bytes.
- Pick the algorithm and output encoding at the top of the right pane. Every algorithm is also listed in the table below it.
- To check a signature you received, paste it into Verify a signature. Values with prefixes like
sha256=, and full Stripe headers, are understood.
Webhook signatures by service
| Service | Header | What is signed | Algorithm and encoding |
|---|---|---|---|
| GitHub | X-Hub-Signature-256 | The raw request body | HMAC-SHA256, hex, prefixed with sha256= |
| Stripe | Stripe-Signature | timestamp + "." + body | HMAC-SHA256, hex, in t=...,v1=... |
| Slack | X-Slack-Signature | "v0:" + timestamp + ":" + body | HMAC-SHA256, hex, prefixed with v0= |
| Shopify | X-Shopify-Hmac-Sha256 | The raw request body | HMAC-SHA256, base64 |
| JWT (HS256) | Authorization: Bearer | base64url(header) + "." + base64url(payload) | HMAC-SHA256, base64url |
Why webhook signatures fail to match
Almost every mismatch comes from signing slightly different bytes than the sender did:
- The body was parsed and re-serialized. Frameworks like Express often parse JSON before your code sees it.
JSON.stringifyof the parsed object can change spacing, key order, and escaping, so the signature no longer matches. Verify against the raw body, for example withexpress.raw({type: 'application/json'}). - A line break was added. Copying a body from a log or terminal often adds a trailing newline. The generator warns you when the message ends with one.
- The secret was decoded wrong. Stripe secrets starting with
whsec_are used as plain text, including the prefix. Other services give you a hex or base64 secret that must be decoded to bytes first. - The encoding doesn't match. Shopify sends base64, while GitHub, Stripe, and Slack send hex. The same signature looks completely different in each.
- The timestamp differs. Stripe and Slack sign the timestamp along with the body, so use the exact value from the request header.
When comparing signatures in your own code, use a constant-time function such as crypto.timingSafeEqual in Node.js or hmac.compare_digest in Python, rather than ==.
Choosing an algorithm
HMAC-SHA256 is the standard choice and what nearly every modern API uses. HMAC-SHA512 produces a longer signature and is often faster on 64-bit machines. HMAC-SHA1 and HMAC-MD5 are still secure as HMACs, despite weaknesses in the plain hash functions, but they're mainly found in older APIs like OAuth 1.0 and some payment gateways. Use a random key at least as long as the hash output: 32 bytes for SHA-256, 64 for SHA-512.
Frequently asked questions
What is an HMAC?
HMAC (hash-based message authentication code) combines a secret key with a message and runs them through a hash function such as SHA-256. Anyone with the key can recompute it, so a matching HMAC proves the message came from someone who knows the key and wasn't changed on the way. It is defined in RFC 2104.
How do I verify a GitHub webhook signature?
Choose the GitHub webhook preset, enter your webhook secret, and paste the raw request body exactly as received. The header value shown should equal the X-Hub-Signature-256 header from the delivery, which you can find under Recent Deliveries in the repository's webhook settings. Paste that header into Verify to confirm.
What's the difference between hex and base64 output?
They are two ways to write the same bytes as text. Hex uses 0 to 9 and a to f, two characters per byte, so an HMAC-SHA256 is 64 characters. Base64 is shorter, at 44 characters for SHA-256. Base64url is base64 with - and _ instead of + and /, and no padding, as used in JWTs.
Can I create a JWT here?
Yes. Choose the JWT preset, enter the secret and a JSON payload, and pick HS256, HS384, or HS512. The signed token appears with its header, payload, and signature in different colors. Paste a token into Verify to check its signature against the secret.
Is an HMAC the same as a hash?
No. A plain hash like SHA-256 can be computed by anyone, so it only detects accidental changes. An HMAC also requires the secret key, so it detects tampering. Hashing the key and message together yourself, as in sha256(key + message), is not a safe substitute.
Is my secret key sent anywhere?
No. The signature is computed by JavaScript in your browser and works offline. Still, avoid pasting production secrets into any website you don't control. Generate a test secret for debugging when you can.
