Skip to content
GreenSnippets

Verify a webhook signature in Node.js

Validate an HMAC-SHA256 webhook against the raw request body with a constant-time comparison and replay window.

JavaScript
import crypto from "node:crypto";

function verifyWebhook({ rawBody, timestamp, signature, secret, toleranceSeconds = 300 }) {
  const sentAt = Number(timestamp);
  if (!Number.isFinite(sentAt)) return false;
  if (Math.abs(Date.now() / 1000 - sentAt) > toleranceSeconds) return false;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.`)
    .update(rawBody)
    .digest();

  let given;
  try {
    given = Buffer.from(signature, "hex");
  } catch {
    return false;
  }
  return given.length === expected.length && crypto.timingSafeEqual(given, expected);
}

How it works

Webhook endpoints are public URLs, so anyone can post to them. Verify that the sender knows the shared secret:

  1. Use the raw body bytes. Parsing then re-serializing JSON changes the bytes and breaks the signature.
  2. Compute HMAC-SHA256(secret, timestamp + "." + body).
  3. Compare with crypto.timingSafeEqual (lengths must match first, or it throws).
  4. Reject timestamps outside a short window to limit replays, and store processed event IDs to make handling idempotent.

The timestamp.body format is a common convention; always follow your provider's documented scheme.