Verify the signature, respond fast, and expect duplicate and out-of-order events.
Verify the signature. The provider signs the payload with a shared secret using HMAC-SHA256. HMAC is a hash that only someone with the secret can create. The provider sends the signature in a header, often with a timestamp. To check it:
- Compute the HMAC over the raw request body bytes. Don't use re-serialized JSON, because its bytes can differ from the original.
- Compare the two signatures with a constant-time function like
crypto.timingSafeEqual. A normal comparison stops at the first different character, and an attacker can measure that time to guess the signature. - Reject old timestamps. This blocks replays, where an attacker sends a real, old webhook again.
Respond fast. Return a 2xx status as soon as you have verified and stored the event. Then process it in a background job. A slow handler causes timeouts, and then the provider retries.
Expect duplicates and disorder.
- Delivery is at-least-once: the same event can arrive more than once. Store each event ID and skip events you have already processed.
- Events can arrive out of order. Before you act, check the current state, or fetch the object from the provider's API.
If you send webhooks yourself, do the same for your users:
- Sign them.
- Retry with exponential backoff, which means doubling the wait between retries.
- Offer a tool to replay events.
- Compute the HMAC over the raw request body with the shared secret.
- Use a constant-time comparison and check the timestamp to block replays.
- Acknowledge the webhook quickly, then process it in the background.
- Delivery is at-least-once, so deduplicate by event ID.
- Handle events that arrive out of order.
Read more