Bali, Indonesia
Insights

Cloudflare Proxy Doesn't Hide Your Origin IP If an Unproxied Subdomain Leaks It

October 29, 2026
0:00
Many site owners believe that once a domain is on Cloudflare and the main records show the orange cloud, the server's real IP is hidden and every request has to pass through the WAF and rate limits. That only holds for the hostnames that are proxied. A single grey-cloud record like mail, ftp, or cpanel that points at the same server publishes the origin IP in public DNS. Once someone has that IP, they can talk to your server directly and Cloudflare never sees the traffic.
Cloudflare proxying is a per-record setting, not a per-zone one. When a record is proxied (orange cloud), Cloudflare answers DNS queries for that name with its own edge addresses. Visitors connect to the edge, the edge applies caching, WAF rules, bot checks, and rate limits, and then it opens its own connection to your origin. The origin IP never appears in the DNS answer. When a record is DNS only (grey cloud), Cloudflare acts as a plain authoritative nameserver. It returns whatever value you stored, which is often the same server IP that hosts the website. Grey records usually exist for a reason. The standard proxy handles HTTP and HTTPS, so services like SMTP, IMAP, FTP, and SSH can't go through it. Mail and FTP records end up grey because they would break otherwise. On cPanel servers, the zone usually also carries auto-created names like cpanel, whm, webmail, webdisk, and autodiscover, all pointing at the server's main IP. When the zone gets imported into Cloudflare, those records come along, and nobody thinks about them again. The result is a domain where www is protected and mail.example.com tells anyone who asks exactly where the server lives.
Attackers don't need to guess your entire zone. Bots routinely query common names like mail, ftp, cpanel, webmail, dev, staging, and direct. Certificate Transparency logs list every hostname you've issued a public certificate for. Passive DNS databases keep old answers long after you change them. If any of these return an IP that isn't Cloudflare's, that IP is a candidate origin. From there, a bot sends HTTP requests straight to the IP, either with the right Host header or with SNI for HTTPS. Your web server sees a normal request for example.com and serves it. Everything you configured at the edge is skipped, because those rules only run on traffic that reaches the edge. That includes WAF managed rules, rate limiting, bot challenges, the attack mode toggle, and even caching. This is why the assumption fails under load rather than on a quiet day. Normal visitors still go through Cloudflare, so the site looks fine and the dashboard looks calm. Meanwhile, a credential-stuffing run against wp-login.php or a scraper hammering uncached pages arrives directly at PHP and MySQL. The server slows down or falls over while Cloudflare shows nothing unusual. A useful first check on the support side is the origin access log. If the client IPs in it aren't from Cloudflare's ranges, the traffic isn't going through the proxy. No edge setting will fix that.
Start by listing what the public DNS actually says. Run these from any machine with dig installed (on Debian and Ubuntu it ships in dnsutils or bind9-dnsutils). Replace example.com with your domain.
BASH
# Apex and www: should return Cloudflare edge IPs if proxied
dig +short example.com A
dig +short example.com AAAA
dig +short www.example.com A

# MX returns hostnames; resolve each one it lists
dig +short example.com MX
dig +short mail.example.com A

# TXT: look for ip4: or ip6: in the SPF record
dig +short example.com TXT
Next, sweep the names that commonly leak. This loop checks both IPv4 and IPv6. AAAA records are easy to forget, and an unproxied IPv6 address exposes the origin just as well.
BASH
for h in www mail ftp cpanel whm webmail webdisk autodiscover cpcalendars cpcontacts dev staging direct origin; do
  printf '%-28s A: %s  AAAA: %s
' "$h.example.com" \
    "(dig +short "h.example.com" A | tr '
' ' ')" \
    "(dig +short "h.example.com" AAAA | tr '
' ' ')"
done
To read the results, you need to know which answers are Cloudflare's. The most reliable method is to compare each IP against Cloudflare's published IP ranges. A quicker hint is to request a web hostname and look at the response headers. Proxied names typically return server: cloudflare and a cf-ray header. Any name that resolves to your server's IP, or to anything outside Cloudflare's ranges, is a leak. The TXT check matters more than people expect. An SPF record such as v=spf1 ip4:203.0.113.10 include:_spf.example.net ~all publishes the origin IP just as clearly as an A record. SPF a and mx mechanisms don't contain the IP themselves, but they point to names that resolve to it. The final test is whether the origin answers direct requests at all. curl --resolve forces the connection to a specific IP while keeping the correct hostname and SNI. That makes it behave like a bot that already knows your origin.
BASH
# Replace 203.0.113.10 with the IP found in the leaking record
curl -sI --resolve example.com:443:203.0.113.10 https://example.com/ | head -n 5

# Compare with the normal path through Cloudflare
curl -sI https://example.com/ | grep -i -E '^(server|cf-ray):'
If the first command returns your site's normal response without any Cloudflare headers, the origin accepts traffic from anywhere. That's the real problem, and it's worth fixing even after the DNS is clean.
Leak sourceWhy it exposes the originTypical change
mail, ftp A records on the web serverNon-HTTP services can't use the standard proxy, so they stay greyMove mail to a separate host or service; drop FTP or move it to SFTP on a different IP
cpanel, whm, webmail, webdiskAuto-created and imported as DNS onlyDelete the unneeded ones; proxy the rest if Cloudflare supports the port, or reach the panel by server hostname over a firewalled port
SPF ip4: with the web server IPThe IP is listed in a public TXT recordSend mail through a relay or mail service and reference its include: instead
Forgotten dev, staging, direct namesLeft grey for testing or debuggingRemove them, or proxy them and put access rules in front
AAAA recordsOften added by the host and never proxiedProxy or remove them, same as A records
Cleaning up DNS is necessary but not enough. Your old IP may already sit in passive DNS history. Internet-wide scanners connect to every public IPv4 address on port 443 and log which certificate each one presents. If your origin serves a certificate for example.com, it can be found without any DNS record at all. Outbound mail sent from the web server, for example WordPress using local PHP mail, also puts the server IP in the Received headers of every message. So treat hiding the IP as one layer and locking down the origin as the actual control. A workable order looks like this:
  1. Proxy every web hostname that can be proxied, and delete records nobody uses. For cPanel service names, check whether their ports are on Cloudflare's list of supported proxy ports before turning on the orange cloud. Otherwise the panel will stop loading.
  2. Separate mail from the web origin. Use a different server, a different IP, or an external mail service, and route the site's outgoing mail through authenticated SMTP to that service. A mail IP is public by design. It just shouldn't be the same machine that serves the website.
  3. Update SPF so it references the mail service instead of the web server's IP.
  4. Restrict inbound 80 and 443 on the origin to Cloudflare's published IP ranges. Use whatever firewall the server already runs (CSF, ufw, nftables, or the provider's network firewall). The exact syntax depends on the tool.
  5. After the cleanup, change the origin IP if you can. The old one is already recorded in history databases, and a new IP that has never appeared in DNS starts clean.
Before applying step 4, keep a way back in. Leave SSH and panel access open from your own trusted IPs, or keep a console session from the provider ready. A firewall rule that only allows Cloudflare on 80/443 should not touch port 22. Cloudflare's ranges also change over time, so load them from the official list instead of copying them from a blog post, and plan how you'll update them. Otherwise, legitimate traffic can start failing months later. If you can't maintain an IP allowlist, there are two alternatives. Cloudflare Tunnel runs a connector on the server that opens outbound connections to Cloudflare, so the origin doesn't need open inbound web ports at all. Authenticated Origin Pulls has the origin require a client certificate that only Cloudflare presents. Both need setup and testing that depend on your plan and web server, so try them on a staging host first. Once the origin rejects anything that doesn't come from Cloudflare, a leaked IP becomes much less useful. Bots can find it, but they get a refused connection instead of your login page. The practical habit is short. Every time a zone gets imported, a panel adds records, or someone creates a test subdomain, run the dig sweep and the curl --resolve test again. The orange cloud protects a hostname, not a server. The server is only protected when it refuses to talk to anyone except the proxy.