For developers
Webhook IP Allowlisting: Security Benefits and Common Mistakes
Use IP allowlists safely for webhooks while avoiding brittle rules, stale provider ranges, and false confidence.
- Author
- Mojahid Ul Haque
- Updated
- Reading time
- 7 min read
What an IP allowlist does for a webhook endpoint
A webhook endpoint is a URL on your server that a provider calls when something happens in their system: a payment settles, a repository receives a push, an order is created. Because it must be reachable from the internet, it also receives scanners, misconfigured clients and anyone who guesses or leaks the URL, and an IP allowlist that admits only the provider's published sender addresses removes most of that noise before your application code runs.
It is worth being precise about what the filter establishes. A matching source address shows that the connection arrived from an address inside the provider's published range. It does not show that the request body is the one the provider sent, that it was not altered in transit by something between the provider and you, or that it is not an old delivery being sent again. Those are properties of the message, and only the message itself can carry evidence for them.
Signature verification comes first
Every major webhook provider signs its deliveries with a keyed hash (HMAC, RFC 2104) computed over the raw request body with a secret that only you and the provider hold. Stripe sends it in the Stripe-Signature header and verifies it against your whsec signing secret; GitHub sends an HMAC hex digest in X-Hub-Signature-256, prefixed with sha256=; Shopify sends a base64-encoded HMAC in X-Shopify-Hmac-SHA256 generated from your app's client secret and the raw body; Twilio signs every request with X-Twilio-Signature. The names differ, the principle does not: recompute the hash over the exact bytes you received and compare it with the header.
Three implementation details decide whether that check is real. Compute over the raw body as received, not over a JSON object you parsed and re-serialised, because any change in key order or whitespace breaks the hash. Compare the two values with a constant-time comparison so that response timing does not leak how many leading bytes matched. And reject before you parse, log or act on the payload, so that an unsigned request never reaches business logic.
Replay protection and duplicates
A valid signature on a captured delivery stays valid, so an attacker who records one request can send it again later. Providers close this gap by signing a timestamp along with the body. Stripe documents the pattern: the signature header carries a timestamp, the libraries reject deliveries whose timestamp is more than a default tolerance of 5 minutes from the current time, and the documentation recommends keeping your clock synchronised with NTP so that honest deliveries are not rejected as stale.
Separately from replay, providers retry deliveries that time out or fail, so your endpoint will receive genuine duplicates. Record the provider's event or delivery identifier and treat a repeat as already processed. The two mechanisms protect against different things: the timestamp window limits how long a captured request stays usable, and idempotent handling makes a repeated request harmless whether it came from the provider or from an attacker inside the window.
Provider ranges change
Published sender ranges are data, not configuration. Stripe lists the IP addresses its webhooks originate from and tells integrators to confirm that events come from one of them. GitHub publishes the ranges it uses to serve content, deliver webhooks and run hosted builds, in CIDR notation, and notes that the list is not exhaustive. AWS goes a step further for its own address space: the current ranges are in ip-ranges.json, and changes are announced on the Amazon SNS topic AmazonIpSpaceChanged, which is the model to copy if you want allowlists that update themselves.
Some providers cannot be allowlisted at all. Twilio states that, because it delivers through a cloud architecture, it does not have a fixed range of IP addresses that issue webhooks, and points integrators at signature validation and HTTP authentication on the webhook URL instead. If a provider's documentation makes no promise about source addresses, an allowlist built from observed traffic will break the first time their infrastructure moves.
Treat the range list the way you treat a certificate bundle: fetch it from the provider on a schedule, diff it, apply it automatically, alert when the fetch fails, and copy any IPv6 prefixes the provider publishes, because a rule set that only carries the IPv4 lines will silently reject deliveries that arrive over IPv6.
Common mistakes and a safe order
The most common failure is the stale list: a range copied into a firewall rule by hand during integration and never touched again. The second is the over-broad rule that allows an entire cloud provider's address space because the sender runs there, which admits every other customer of that cloud; the data-center and residential guide explains why a hosting range says nothing about who is using it. The third is checking the wrong address: behind a load balancer, reverse proxy or CDN the address on the socket belongs to the intermediary, and the real client address arrives in a forwarded header that only your own edge may be trusted to set, as the X-Forwarded-For guide explains. The last is treating the allowlist as the security control and skipping signature verification, so that a leaked URL plus any machine inside the provider's range is enough to inject events.
The safe order follows: terminate TLS and reject plain HTTP; verify the signature over the raw body and stop on failure; check the signed timestamp against a tolerance of a few minutes with a synchronised clock; return success for an event identifier you have already handled; and only then apply the source-address check as an additional layer, built from an automatically refreshed copy of the provider's list, having decided in advance whether a mismatch rejects the delivery or only raises an alert. Log the source address, signature outcome and event identifier for every delivery, including rejections: a rise in rejections with valid signatures from unlisted addresses almost always means the provider changed ranges before your copy updated, and an IP lookup on the rejected address shows whether it belongs to the provider's hosting network or somewhere unexpected.
Sources
- RFC 2104: HMAC: Keyed-Hashing for Message Authentication
- Stripe: Receive Stripe events in your webhook endpoint (signatures, replay attacks, tolerance)
- Stripe: Domains and IP addresses
- GitHub Docs: Validating webhook deliveries
- GitHub Docs: About GitHub's IP addresses
- Shopify: Verify webhook deliveries
- Twilio: Secure webhooks
- AWS: AWS IP address ranges (ip-ranges.json)
- AWS: AWS IP address ranges notifications (AmazonIpSpaceChanged)
Frequently asked questions
Keep reading
Related guides
- For developers9 min read
API Rate Limiting by IP: Benefits, Limits, and Safer Rules
Design better API limits by combining IP, token, account, and organization-level controls.
Updated
- Fraud and risk scores5 min read
How to Read Server Logs and Identify Malicious Bot IPs
Which log fields expose automated traffic, how to group requests by IP and ASN, and when to confirm a suspect address with a reputation lookup before blocking it.
Updated
- IP location and accuracy4 min read
How to Track Public IP Geolocation Safely and Clearly
What public IP geolocation can and cannot show, how to read the result responsibly, and where it helps in security investigations and troubleshooting.
Updated
- ISP, ASN and network ownership5 min read
Why a Website Shows Cloudflare IP Addresses Instead of the Origin Server
A site behind Cloudflare or another CDN resolves to the CDN's addresses, not its own server. What that hides, what it does not, and how to read the lookup.
Updated