What is timestamp tolerance?
Timestamp tolerance is the maximum age a webhook's signed timestamp may have, relative to the receiver's clock, before the receiver rejects the message. A delivery carrying a timestamp outside the window fails verification even when its signature is valid. The check exists for one reason: a signature proves who sent a message and that it wasn't modified, but nothing about when it was sent, and that gap is what replay attacks exploit.
How the check works
Providers following the Standard Webhooks specification send a webhook-timestamp header holding the Unix timestamp of the attempt, and include that timestamp in the content that gets signed: the HMAC is computed over id.timestamp.payload. The receiver verifies in two steps. First it checks the signature over those exact bytes; then it checks that the timestamp is within the allowed tolerance of its own current time, in either direction.
Signing the timestamp is what makes the freshness check trustworthy. An attacker who captures a legitimate delivery and re-sends it later keeps a valid signature but a stale timestamp, so the tolerance check drops it. An attacker who rewrites the timestamp to the present invalidates the signature, because the signed bytes changed. The two checks only work together: an unsigned timestamp header could be freely forged, and a signature without a freshness check validates forever.
Choosing the window
The spec requires the timestamp to be within "some allowable tolerance" of the current time and leaves the number to the implementation. Five minutes is the de facto standard: Svix's and Stripe's verification libraries both ship with it, and most others land in the same range.
The window is a tradeoff between two failure modes. Too wide, and a captured message stays replayable for the whole width. Too narrow, and legitimate traffic starts failing: slow proxies add seconds, and clock skew between provider and receiver adds more. Five minutes is generous enough that a healthy system never notices it and narrow enough that a replay has to happen nearly in real time. Within that window, deduplication on the message ID closes what the tolerance cannot.
Clock skew, the operational gotcha
The tolerance check compares the provider's clock against yours, so it fails in a distinctive way when a receiver's clock drifts: signatures verify everywhere else, but every delivery to one host is rejected as too old or, more confusingly, as timestamped in the future. This is why the check accepts skew in both directions, and why receivers should run NTP on anything that verifies webhooks. When an integration suddenly rejects all traffic with timestamp errors, check the clock before the code.
A related, deliberate consequence: messages redelivered long after the fact, from a dead letter queue or a manual replay, carry the timestamp of the new attempt, not the original one. Providers set the timestamp per attempt precisely so that legitimate retries and replays pass the freshness check.
Where the check lives
Verification libraries from spec-following providers perform both checks together; Svix's libraries reject anything outside five minutes by default. If you are verifying by hand, the tolerance check is one comparison, but it belongs with the signature check, not after business logic, so that stale messages are refused before anything acts on them. The webhook security entry places the check in the full receiving pipeline.
Frequently asked questions
What is the standard timestamp tolerance for webhooks?
Five minutes is the de facto default: Svix's and Stripe's libraries ship with it, and the Standard Webhooks specification requires a tolerance while leaving the exact value to the implementation. Almost all providers land between one and ten minutes.
Why did my webhook fail with a timestamp error?
Either the message is genuinely old, such as a capture being replayed, or, far more commonly, the receiving server's clock has drifted outside the tolerance window. If every delivery to one host fails while others succeed, fix the clock with NTP before touching the code.
Does the timestamp tolerance stop all replay attacks?
It stops replays after the window closes. A capture replayed within the window still verifies, which is why receivers also deduplicate on the message ID. The tolerance plus deduplication together close the replay gap.
Can I just widen the tolerance to fix verification failures?
You can, but every minute of extra width is a minute longer that a captured message stays replayable. Widening the window to paper over clock skew treats the symptom; syncing the receiver's clock with NTP fixes the cause without weakening replay protection.
Ready to send webhooks?
Svix handles signing, retries, rate limiting, and delivery observability for the webhooks you send to your users, so your team can stay focused on your product.
Start sending webhooks with Svix or read the build vs. buy analysis