Webhook Security Best Practices
Secure a webhook by signing every request with HMAC-SHA256 over the payload plus a timestamp, rejecting any request whose signature fails verification or whose timestamp is more than five minutes old, sending only over HTTPS, and routing outbound calls through a proxy that blocks internal IP ranges. Receivers should treat delivery as at-least-once and deduplicate by idempotency ID.
How to secure webhooks
Four controls cover the attacks webhooks are actually subject to:
- Sign the request. Compute an HMAC-SHA256 over the raw body, the timestamp, and a message ID, using a secret shared with that one endpoint, and send it in a header. Receivers recompute it and compare with a constant-time equality check. This is the scheme the Standard Webhooks specification, which Svix co-created, defines in full, headers and encoding included.
- Include and check a timestamp. A five-minute tolerance is the common choice: old enough for clock skew, short enough that a captured request expires before it is useful.
- Filter outbound destinations. Resolve the endpoint URL and refuse private and link-local ranges, and run the sending workers where they cannot reach internal services.
- Require HTTPS. Reject
http://endpoints at registration rather than at send time, so the misconfiguration is caught by the person who can fix it.
The sections below explain what each control defends against, and webhook security 101 has the requests to send your own endpoint to confirm each one works.
Server-side request forgery (SSRF)
A Server-Side Request Forgery (SSRF) attack is when an attacker manipulates a server into reading or updating internal resources by supplying or altering a URL, which the server then calls. Webhook implementations are particularly vulnerable to SSRF as they allow consumers to add any URL they want, which will be accessed from the internal webhook system.
Preventing SSRF attacks primarily involves preventing webhooks from communicating with internal networks and services.
Best practices for preventing SSRF:
- Using a special proxy such as Smokescreen to filter internal IP addresses
- Placing the webhook workers (or proxy) in a separate private subnet with no access to internal services
Replay attacks: duplicating data transmissions
Replay attacks can be particularly problematic. A bad actor captures a valid request and sends it again later; because the copy is unmodified, its signature still verifies.
Best practices for preventing replay attacks
- Add a timestamp to each webhook attempt, include it in the signed content as the Standard Webhooks spec requires, and ensure that it falls within a specific tolerance.
- Implementing idempotency in your APIs can help ensure that webhook requests are only processed once, even if they are received multiple times.
Spoofing attacks
Attackers can impersonate services by sending a fake webhook to an endpoint, turning the very essence of webhooks, a simple HTTP POST, into a security vulnerability. The best way to prevent spoofing attacks is to sign your webhook requests. You can even take advantage of the timestamp and idempotency IDs that help with replay attacks.
Best practices for preventing spoofing attacks with webhook signatures:
- Include the payload and additional metadata like an idempotency ID and timestamp when signing the webhook
- Use HMAC-SHA256
Protecting against man-in-the-middle attacks
Man-in-the-middle (MITM) attacks occur when an attacker intercepts communications between two parties.
Best practice for preventing Man-in-the-Middle attacks:
- Always use HTTPS URLs, ensuring that the request is encrypted and the connection verified.
Webhook security risks and the control that closes each one
Webhook security comes down to five risks: forged requests to a public URL, payloads altered in transit, captured deliveries replayed later, outbound requests steered at internal services, and secrets that leak from logs or linger after rotation. Each has one specific control, and a service that applies all five on every endpoint is secure in practice.
| Risk | How it happens | Control |
|---|---|---|
| Forged request | Anyone who learns the endpoint URL can POST to it | HMAC-SHA256 signature with a per-endpoint secret, verified before the body is parsed |
| Tampered payload | A proxy or attacker changes bytes between sender and receiver | Signature computed over the raw body, compared in constant time, sent over HTTPS only |
| Replayed delivery | A captured valid request is sent again later | Signed timestamp checked against a five-minute tolerance, plus deduplication on the message ID |
| SSRF | A consumer registers a URL that resolves to an internal service | Resolve the URL and refuse private and link-local ranges; send from workers with no internal network access |
| Leaked or stale secret | Secrets appear in logs, or rotation drops in-flight deliveries | One secret per endpoint, rotated with an overlap window during which both are accepted, as secret rotation describes |
Putting the controls together
None of these controls is difficult on its own. The work is applying all four consistently, on every endpoint, for as long as the integration lives, including secret rotation and the audit trail that shows which delivery carried which signature. Svix implements this set by default, and Svix Play lets you inspect the signed requests your own service sends.