Apex Domains for Cloudflare for SaaS
TL;DR: want your customers' apex domains working on the Cloudflare for SaaS setup you already have? Sign up for the beta. Read on if you're curious how it works.
Cloudflare for SaaS is great, and it takes away a lot of the pain around SSL provisioning for custom domain onboarding flows. The biggest limitation: it doesn't support apex domains out of the box, because most DNS providers won't let you put a CNAME at the zone root, a restriction from the DNS spec itself, not something Cloudflare invented. If you're like us, you want your customers to be able to point their apex domain at your product on the Cloudflare for SaaS setup you already have, without hand-rolling workarounds for it.
Our goal was to stay on Cloudflare for SaaS. First we tried a few different kinds of workarounds: detecting where a customer's domain was actually hosted, then hand-picking a fix for whichever registrars we could. That meant solving for domain registrars one at a time, adding more and more complexity to domain onboarding, the most important step of our application. It worked, for a while. But there was always another edge case, and the workflow kept getting more complicated than we wanted to maintain. At some point it was clear the whole approach needed to be simpler, not more clever.
We looked at leaving Cloudflare for SaaS entirely, but we didn't want to give up the Workers logic and provisioning we'd already built there. So instead we built an SNI-based TCP proxy: something that reads the hostname straight out of the TLS handshake and routes on it, without ever terminating SSL itself. A few proof-of-concepts later, the mechanism held up. The harder part was making it resilient enough to actually carry customer traffic: a global edge on an anycast set of IPs, so a visitor lands on the nearest healthy location instead of one box somewhere.
What that adds up to: a set of IPs your customer points their apex domain at. Every request lands on that edge, gets routed at the TCP layer by hostname, and goes straight back to Cloudflare, unmodified. Cloudflare still terminates TLS and runs your setup exactly as it does today. We keep everything we'd already built on Workers, and your customers finally get apex domain support on the setup you already have.
After testing it with a few real customer domains, we decided to open it up as a feature. We think it's a natural extension of our redirect platform, the same one already handling domain migrations, subdomain routing, and multi-domain setups for agencies. Here's a demo of the workflow, and how our solution makes it simpler.
How many customers have you lost to apex domains you can't support?
One DNS record, and real output proving Cloudflare is the one actually serving it.
That's the whole setup: an ordinary A record at the root, pointed at a static IP we give you. Nothing new for a registrar to support, every one of them has always allowed this.
What actually happens
$ dig +short A example.com
203.0.113.42
↳ The apex domain resolves straight to that static IP, like any other A record.
$ curl -sI https://example.com | grep -i server
server: cloudflare
↳ Cloudflare answered the request, not us. We just forwarded the bytes.
$ openssl s_client -connect example.com:443 -servername example.com | openssl x509 -noout -issuer
issuer: Google Trust Services (for example.com)
↳ Cloudflare issued and holds this certificate, same as any other Cloudflare for SaaS hostname. We never see the private key.
Simulated for illustration. Your real setup shows your real domain and IP here.
How it works
We hand you a static IP
One or two IP addresses, dedicated to routing traffic for your customers' apex domains.
Your customer points their apex A record at it
No CNAME at the root required. This is the part their DNS provider was blocking in the first place.
We forward straight into your Cloudflare for SaaS setup
Routed by the hostname in the TLS handshake, not decrypted or inspected. Your certs and rules stay exactly as they are.
The proxy logic
The mechanism itself isn't much code, and we're not precious about it. Here's the actual relay: it resolves your Cloudflare for SaaS hostname, opens an outbound connection for every inbound one, and pipes bytes in both directions. No TLS termination, no inspection, nothing clever. We're not putting this on GitHub or maintaining it as a project, just dropping it here. Do what you want with it.
const net = require('net')
const dns = require('dns').promises
const TARGET_HOST = 'proxy.yourdomain.com' // your Cloudflare for SaaS zone
const PORTS = [80, 443]
let cachedIp = null
let cachedAt = 0
const CACHE_MS = 60_000
async function resolveTarget() {
const now = Date.now()
if (cachedIp && now - cachedAt < CACHE_MS) return cachedIp
const [ip] = await dns.resolve4(TARGET_HOST)
cachedIp = ip
cachedAt = now
return ip
}
function startRelay(port) {
net.createServer(async (client) => {
let targetIp
try {
targetIp = await resolveTarget()
} catch {
return client.destroy()
}
const upstream = net.connect(port, targetIp, () => {
client.pipe(upstream)
upstream.pipe(client)
})
upstream.on('error', () => client.destroy())
client.on('error', () => upstream.destroy())
}).listen(port, '0.0.0.0')
}
PORTS.forEach(startRelay)That's genuinely most of the idea. The hard part was never the pipe, it was everything around making that pipe trustworthy enough to put in front of a real customer's traffic:
- ✓A static IP that survives a box getting replaced. If the instance behind it dies, swapping in a new one can't change the address your customer already put in their DNS.
- ✓Real health checks, not port checks. Ours makes an actual HTTPS request through the relay every couple of minutes and forces a replacement on repeated failure, before a customer notices anything.
- ✓Load testing against real infrastructure, not a guess. We pushed it past thousands of concurrent connections before trusting it with a single domain.
- ✓A real look at DDoS exposure. A single box on the open internet, like the code above, is an easy target, and we won't pretend otherwise. That's exactly why production doesn't look like that: Anycast IPs spread the same address across many locations so an attack on one doesn't take the address down, on top of the other DDoS mitigations built into the infrastructure behind it.
- ✓Monitoring and alerting on all of the above, every hour of every day, so a failure gets fixed before it's a support ticket.
None of that shows up in thirty lines of code, and none of it is a one-time cost. It's the ongoing work of running this reliably for domains that aren't ours, which is the actual thing you'd be paying for: not the trick, but the operations behind it, already built and already running.
Want in?
If you'd rather skip building and running all of that yourself and just get your customers apex A record support on top of the Cloudflare for SaaS setup you already have, you're in luck. We're opening this up to a few customers to try out and get their feedback. Let us know you're interested by filling out the form below and we'll be in touch about onboarding.
Pricing
$0.20 / apex domain / month
Double the $0.10 per-hostname unit Cloudflare already charges for a custom hostname, extended to the apex domains that would otherwise fail verification over a plain A record. No setup fee, no minimum commitment.
That $0.20 is a baseline, not a flat rate covering unlimited traffic. Every account gets a set amount of bandwidth included at no extra cost, with any overage billed based on your tier. We're still learning what real bandwidth use looks like across different customers, so both the included amount and the overage pricing are subject to change as we get more data.
Early access
This is new, and we're onboarding a small group of customers by hand before opening it up further. Leave your email and roughly how many apex domains you'd need it for, and we'll reach out.
Frequently asked questions
Why does Cloudflare for SaaS need help with apex domains at all?+
Most DNS providers won't let you put a CNAME at the zone root, that's a DNS-spec restriction, not a Cloudflare one. An ordinary A record has always worked at the apex, so that's what we give your customer: a static IP to point their A record at. It sits in front of the exact Cloudflare for SaaS setup you already have; nothing about it changes.
Does this replace Cloudflare for SaaS?+
No. It sits in front of it for one purpose: giving your customer an IP address to point their apex A record at. Cloudflare for SaaS still issues the certificate, still terminates TLS, and still runs whatever routing you already have set up. Nothing on your side changes.
Do you see or store our customers' traffic?+
No. We route on the hostname sent in the TLS handshake (SNI), not on the contents of the request. We never terminate TLS and never decrypt anything that passes through.
What if a customer's DNS provider already supports CNAME flattening at the apex?+
Then you don't need this for them. It only matters for the customers stuck on a registrar or DNS provider that requires a literal A record at the root, which is most of the ones that trigger a support ticket in the first place.
Is this ready for production traffic today?+
We're hand-onboarding a small group of early customers rather than opening this up broadly. If your volume is meaningful, tell us in the form below and we'll talk through your setup before you rely on it.
Is it globally connected and redundant?+
Yes. Visitors get routed to the nearest available location automatically, and if one location ever has a problem, traffic just moves to another. Nothing for you or your customer to set up for that, it just works that way by default.
How is this priced?+
Twenty cents per apex domain per month as a baseline, double the $0.10 Cloudflare already charges per custom hostname, so there’s real margin on it rather than a pure pass-through. Every account gets a set amount of bandwidth included at no extra cost, with overage billed based on your tier. No setup fee, no minimum, and no annual contract to join early access. Both the included amount and the overage pricing are subject to change as we learn more about real bandwidth use.