HMAC generator and webhook validator

Compute HMAC-SHA256, SHA-384, SHA-512 or SHA-1 and check whether the signature a webhook sent matches. The GitHub, Stripe, Shopify, Mercado Pago and Slack presets build the signed message the way each provider does. Payload and key stay in your browser.

Provider

HMAC of the payload as is, with the algorithm and encoding you choose.

The signature covers the raw body, byte for byte. A different space, line break or field order changes the result. Paste the body exactly as it arrived, before it went through JSON.parse or a formatter.

The key is
Algorithm
Signature format

Signature

The key is missing

Enter the secret key. The signature appears as you type.

An HS256 JWT is an HMAC-SHA256 of the header and payload. The decoder checks the token's signature with your secret.

Decode JWT

Want to read the webhook payload? Format it in a separate tab. Formatted JSON has different bytes and can't be used to check the signature.

Format JSON

Just need a hash, without a key? The hash generator computes MD5, SHA-1 and SHA-256 of text and files.

Generate hash

What is HMAC

HMAC is a hash computed with a secret key (RFC 2104). Anyone can recompute a plain SHA-256 after changing the message. With HMAC, only whoever knows the key can produce the right signature. That's why it's used to prove a request came from who it says and wasn't changed along the way: webhooks, API signing such as AWS Signature V4 and HS256 JWTs.

Each provider's format

Each provider chooses what it signs and how it writes the result. The table sums up this page's presets; each format was checked against the provider's official documentation.

ProviderHeaderWhat is signedFormat
GitHubX-Hub-Signature-256bodysha256=<hex>
StripeStripe-Signaturet.bodyt=<ts>,v1=<hex>
ShopifyX-Shopify-Hmac-Sha256body<base64>
Mercado Pagox-signatureid:…;request-id:…;ts:…;ts=<ts>,v1=<hex>
SlackX-Slack-Signaturev0:timestamp:bodyv0=<hex>

How webhook verification works

The provider and your application share the same secret. When sending the event, the provider computes the HMAC of the body, or of a message built from a timestamp and the body, and sends the result in a header. Your application recomputes it with the received body and compares. If they match, the event is authentic. The comparison must run in constant time, with crypto.timingSafeEqual in Node, hash_equals in PHP or hmac.compare_digest in Python, so the response time doesn't leak the signature.

The number one cause of invalid signatures

The framework turns the JSON into an object before your code runs, and serializing it again changes key order, spacing and the escaping of accented characters. The signature stops matching even with the right key. Read the raw body: express.raw() in Express, $request->getContent() in Laravel, a String parameter with @RequestBody in Spring and request.get_data() in Flask. In NestJS, create the app with rawBody: true and use req.rawBody.

Key as text, hex or Base64

HMAC works with the key's bytes. If the provider's dashboard shows a hex secret and you use that string as text, the bytes are different and so is the signature. Most webhook providers, including GitHub, Stripe, Shopify, Mercado Pago and Slack, use the secret as text, exactly as it appears in the dashboard, including Stripe's whsec_ prefix.

Timestamps and replays

A captured valid request stays valid if it's sent again. Stripe and Slack put the timestamp inside the signed message, and their libraries reject events older than 5 minutes. When testing with an old payload copied from a log, the signature may match here and still be rejected by your server because of the time.

Frequently asked questions about HMAC

Webhook signatures, keys, encodings and the most common validation mistakes.