Cloudflare publishes the IP ranges its proxy uses to reach your origin, and the correct use of that list is a firewall rule at the origin that allows those ranges on ports 80 and 443 and denies everyone else, so that attackers cannot bypass the proxy by hitting your server’s address directly. The list is at cloudflare.com/ips and via a plain-text endpoint that scripts can pull, it changes occasionally, and a stale copy is an outage. The IDs half of the query is usually the Ray ID, the identifier Cloudflare stamps on every request in the cf-ray header and on every error page, which is what you quote when you need to trace a specific request through their logs.
Both halves of this are small, mechanical tasks with one trap each. For the IP ranges the trap is believing the allowlist is a complete security boundary. For the Ray ID the trap is expecting it to tell you something on its own; it is a lookup key, not a diagnosis.
Table of contents
- Why the origin needs an allowlist at all
- The published ranges and how to fetch them
- Allowlisting on ufw, nftables and a cloud security group
- Why allowlisting alone is not enough
- Getting the real client IP behind the proxy
- The Ray ID: what it is and what it is for
- How this fits the rest of the stack
- FAQ
Why the origin needs an allowlist at all
Putting a site behind Cloudflare’s proxy hides the origin’s IP address from DNS. It does not hide it from anyone who found it another way: an old DNS record, a certificate transparency log, an email header, a misconfigured subdomain that was never proxied. Once an attacker has the address, they can send requests straight to it and every layer Cloudflare provides, the WAF, the rate limits, the bot filtering, the DDoS absorption, is simply not in the path.
The fix is at the origin: accept web traffic only from the addresses Cloudflare’s proxy uses. Anything arriving from elsewhere is either a bypass attempt or a scanner, and it gets dropped before it reaches the web server.
The published ranges and how to fetch them
Cloudflare maintains the definitive list on its IP ranges page and serves the same data as plain text, one CIDR per line, at two well-known URLs. Do not copy the ranges from a blog post, including this one, because the list is versioned and changes.
curl -s https://www.cloudflare.com/ips-v4
curl -s https://www.cloudflare.com/ips-v6
At the time of writing the IPv4 list is fifteen blocks, including large allocations such as 104.16.0.0/13, 104.24.0.0/14, 172.64.0.0/13 and 162.158.0.0/15, and the IPv6 list is seven. The exact set is not the point; the endpoint is. Any firewall recipe should pull from it, not embed it.
Allowlisting on ufw, nftables and a cloud security group
The shape is identical everywhere: allow the fetched ranges on 80 and 443, then set the default policy for those ports to deny. SSH and anything else you need from your own addresses stays on its own rules.
#!/usr/bin/env bash
# ufw: allow Cloudflare ranges on web ports, deny the rest
set -euo pipefail
for ip in $(curl -fsS https://www.cloudflare.com/ips-v4) $(curl -fsS https://www.cloudflare.com/ips-v6); do
ufw allow proto tcp from "$ip" to any port 80,443 comment "cloudflare"
done
ufw deny proto tcp from any to any port 80,443
ufw reload
With nftables, put the ranges into a named set and reference it from one rule, which makes refreshing the list a matter of flushing and repopulating the set. On a cloud provider’s security group, create an inbound rule per range for 443 and 80 and remove the 0.0.0.0/0 rule that most defaults ship with. Whichever tool you use, run the refresh on a schedule, weekly is plenty, and have it fail loudly if the fetch returns an empty list, because an empty allowlist plus a deny policy is a self-inflicted outage.
Why allowlisting alone is not enough
Every Cloudflare customer’s proxied traffic egresses from the same shared ranges. An allowlist proves a request came through Cloudflare’s network. It does not prove it came through your zone’s configuration. An attacker can put your origin’s IP behind their own Cloudflare zone, with no WAF rules and no rate limits, and their requests arrive from an allowed range.
Two additions close that hole.
- Authenticated Origin Pulls. Cloudflare presents a client certificate when connecting to your origin, and the origin’s web server is configured to require it. Requests through someone else’s zone do not carry your zone’s certificate and are rejected at the TLS handshake.
- Host header validation. The web server only serves requests whose
Hostheader matches your hostnames and returns 4xx to anything else, which stops the origin from being used as a generic backend.
With all three in place, allowlist, mutual TLS and host validation, the origin is only reachable by Cloudflare acting on behalf of your zone, which is what the allowlist was supposed to mean in the first place.
Getting the real client IP behind the proxy
A side effect of proxying is that the source address your web server sees is Cloudflare’s, so access logs, rate limiting and geo rules at the origin all key on the wrong value. Cloudflare sends the visitor’s address in the CF-Connecting-IP header. The web server should be told to trust that header only when the connection came from an allowed range, which is exactly the list above; trusting it from anywhere lets a direct requester spoof their address.
# nginx: trust CF-Connecting-IP only from Cloudflare ranges
set_real_ip_from 104.16.0.0/13;
# ...one line per range from the fetched list...
real_ip_header CF-Connecting-IP;
The Ray ID: what it is and what it is for
Every request that passes through Cloudflare gets a Ray ID, a sixteen-character hex string with a data centre code appended, such as 8a1b2c3d4e5f6a7b-LHR. It is returned to the client in the cf-ray response header, shown on Cloudflare-generated error pages, and recorded in Cloudflare’s own logs.
The Ray ID identifies one request at one edge location. It does not encode anything about the cause of an error. Its purpose is correlation: a visitor reports a 5xx page and reads you the Ray ID, you search for it in the zone’s request logs or quote it to support, and the trail leads to the specific request, the rule that matched, the origin response code and the timing. Logging cf-ray in your origin access logs beside your own request ID is worth the one line of configuration, because it joins Cloudflare’s view of a request to yours.
Other identifiers people search for under the same phrase: the zone ID and account ID, both found in the dashboard’s overview sidebar and used only for API calls, and the cf-cache-status header, which is not an ID but is the value most useful for understanding why a response was slow or stale.
How this fits the rest of the stack
An origin allowlist only matters when there is an origin you administer. On a platform that terminates TLS and serves the site or service for you, the question shifts to which records to proxy and which to leave as DNS only, and custom domains on RunxBuild document that per record type. For a service with its own firewall needs, the ranges endpoint above is still the source of truth. To see what the origin, the database behind it and the bandwidth through the proxy add up to, the RunxBuild hosting calculator shows them as separate lines rather than a single hosting figure.
Useful related references:
- Cloudflare Logpush: What It Ships, What It Costs, and What It Misses
- Cloudflare TLS Termination: The Encryption Modes and the One That Is a Trap
- namecheap with cloudflare: The Three Modes and the One That Actually Saves Money
- Services on RunxBuild
FAQ
Where do I get the current list of Cloudflare IP ranges?
From the endpoints Cloudflare publishes: https://www.cloudflare.com/ips-v4 and https://www.cloudflare.com/ips-v6, which return one CIDR per line. The IP ranges page on their site shows the same list. Scripts should fetch it rather than embed a copy, because the list changes.
Is allowlisting Cloudflare IPs enough to protect my origin?
No. The ranges are shared by every Cloudflare customer, so a request from an allowed range only proves it passed through Cloudflare’s network, not through your zone’s rules. Add Authenticated Origin Pulls so the origin requires Cloudflare’s client certificate, and validate the Host header.
How often should I refresh the allowlist?
Weekly is sufficient; the list changes a few times a year. Automate the refresh, and make the script abort if the fetch returns an empty result, because an empty allowlist combined with a deny rule blocks all web traffic.
What is a Cloudflare Ray ID?
A unique identifier stamped on every request that passes through Cloudflare, returned in the cf-ray header and shown on Cloudflare error pages. It identifies one request at one data centre and is used to look that request up in logs or to give support a reference. It does not describe the error itself.
How do I see the real visitor IP behind Cloudflare?
Read the CF-Connecting-IP header, and configure the web server to trust it only for connections from Cloudflare’s ranges, for example with nginx’s set_real_ip_from and real_ip_header directives. Trusting the header from any source lets direct requesters spoof their address.