ToolSec

🔐 Crypto & Encoding

HMAC Explained: How Webhook Signatures Work

· 6 min read · Updated June 27, 2026

If you've integrated a webhook from Stripe, GitHub, Slack or similar, you've met HMAC — usually as a header like X-Hub-Signature-256 you're told to verify. This guide explains what HMAC actually is and how to check those signatures correctly.

What is HMAC?

HMAC (Hash-based Message Authentication Code) combines a hash function with a secret key to produce a tag that proves two things at once: the message wasn't altered (integrity) and it was produced by someone who knows the key (authenticity). You'll see it written as HMAC-SHA256, meaning HMAC built on the SHA-256 hash.

Why not just a plain hash?

A plain hash like SHA-256(message) only proves the message hasn't changed accidentally — anyone can recompute it, so it proves nothing about who sent it. HMAC mixes the secret key into the computation, so only parties who share the key can produce or verify a valid tag. That difference is the whole point: HMAC authenticates the sender, a bare hash does not.

How webhook signing works

When a provider sends you a webhook, they compute HMAC(your_signing_secret, raw_request_body) and put the result in a header. Your endpoint repeats the calculation with the same secret and the exact bytes you received, then compares. If the two match, the request genuinely came from the provider and wasn't tampered with in transit. If they don't, you reject it.

  1. The provider signs the raw payload with the shared secret and sends the signature in a header.
  2. You read the raw request body — before any parsing or re-serialising.
  3. You compute HMAC over those exact bytes with your copy of the secret.
  4. You compare your result to the header using a constant-time comparison.

Two mistakes that break verification

  • Hashing the re-serialised body. If your framework parses JSON and you then re-stringify it, the bytes can differ (key order, spacing), and the HMAC won't match. Always sign and verify the raw body.
  • Using a normal string comparison. A naive == can leak timing information that helps an attacker forge a signature. Use a constant-time comparison function from your crypto library.

HMAC and JWTs

You've already met HMAC if you've used JWTs: the signature on an HS256 token is literally an HMAC-SHA256 over the token's header and payload. Same primitive, different application. See our guide to JWTs for that side of the story.

Choosing the algorithm and key

Use SHA-256 or stronger; HMAC-SHA1 is still cryptographically acceptable for compatibility but SHA-256 is the modern default. The security rests on the key, so use a long, random secret — not a human-chosen word. Match the output format (hex or Base64) to whatever the provider specifies.

Try it

Compute HMACs in hex or Base64 with different algorithms and keys using our HMAC generator — paste a sample body and secret to reproduce exactly what a provider would send. To build the JWT side, use the JWT generator.

Frequently asked questions

What is the difference between a hash and an HMAC?

A plain hash only proves integrity and can be recomputed by anyone. HMAC mixes in a secret key, so it also proves authenticity — only someone with the key can produce or verify it.

How do I verify a webhook signature?

Compute an HMAC over the exact raw request body with your signing secret and the provider's algorithm (usually SHA-256), then compare it to the signature header using a constant-time comparison.

Why does my webhook signature check fail?

The most common cause is hashing a re-serialised body instead of the raw bytes the provider signed. Parsing and re-stringifying JSON can change spacing or key order, breaking the match.

Try the related tools

Related guides