Skip to main content
Security

Webhook signature verification with HMAC: verify bytes before parsing

Webhook signatures prove that a request holder knew a shared secret. Verify the raw payload, use constant-time comparison, enforce timestamp tolerance, and make retries safe.

Thien Nguyen
By Thien Nguyen
Updated May 13, 2026 · 1 min read

A webhook endpoint on the public internet will receive requests that did not come from your provider. Signature verification is the line between processing a payment event and letting anyone invent one.

Short answer: compute an HMAC over the exact raw request bytes using the provider's signing secret, compare it with the supplied signature in constant time, reject stale timestamps, then parse and process the event idempotently.

const expected = createHmac("sha256", secret).update(rawBody).digest();
if (!timingSafeEqual(expected, receivedSignature)) throw new Error("invalid signature");
CheckStops
Raw-body HMACModified or forged payloads
Constant-time compareSignature timing leaks
Timestamp toleranceRecorded request replay
Event ID storageProvider retries causing duplicate work

Parse JSON after verification. Re-serializing parsed JSON can change whitespace or key ordering and makes the bytes differ from what the provider signed.

Return a fast success after persisting the event or queueing it; process slow work asynchronously. A webhook endpoint must be both authentic and retry-safe—HMAC handles only the first half.

Cover photo by Laura Gigch on Pexels.

References

Primary documentation and specifications checked when this article was last updated.

SecurityWebhooksAPIs

Related articles

All articles