Skip to content
Latchkey

How to Verify a Webhook Signature With HMAC SHA-256

Never trust a webhook until its X-Hub-Signature-256 header matches an HMAC SHA-256 you compute over the raw body with your secret.

Compute HMAC-SHA256(secret, rawBody) and compare it to the X-Hub-Signature-256 header using a constant-time comparison. Verify on the raw bytes before any JSON parsing.

Steps

  • Capture the raw request body before parsing it as JSON.
  • Compute sha256= + HMAC of the raw body keyed by the shared secret.
  • Compare to X-Hub-Signature-256 with crypto.timingSafeEqual.
  • Reject with 401 on any mismatch, missing header, or missing secret.

Handler

handler.js
const crypto = require('crypto');

function verify(secret, rawBody, header) {
  const expected =
    'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(header || '');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Gotchas

  • Hash the raw body, not a re-serialized object; re-encoding changes byte order and whitespace.
  • Use timingSafeEqual, never ===, to avoid a timing side channel.
  • Length-check before the compare, since timingSafeEqual throws on unequal lengths.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →