Cloudflare inside your panel: DNS, cache, and WAF without the tabs
How we collapsed three browser tabs into one surface — and the three interesting problems we had to solve to stop fighting Cloudflare's API.
Most of our users don't want to think about Cloudflare. They want their site to be fast and secure and for the WAF to not block their own login page. They also don't want to leave the xCloud panel, open a second tab, remember their Cloudflare password, find the right zone, scroll past sixteen features they don't use, and then come back to finish what they were doing.
So we built Cloudflare into the panel. DNS records, cache rules, purges, WAF posture — all sitting next to the site they belong to, not behind a link.
That decision sounded like "wrap their API, add UI, ship". It turned out to be three harder problems pretending to be one easy one.
Problem 1 — Whose zone is it, really?
Cloudflare's model is account → zone → records. Ours is customer → site → domain. The mapping is not one-to-one. A site can have multiple domains; a customer can point the same domain at multiple sites over time; a zone in Cloudflare can be owned by us, by them, or by a third party we're proxying through.
The naive answer is: store a zone ID on the site. That falls apart the first time a customer moves the domain or changes ownership.
What we landed on:
- Zones are a first-class entity on our side, not a field hanging off a site.
- Each zone records ownership mode:
xcloud,customer, orthird-party. Everything else flows from that. - We never assume the zone in Cloudflare hasn't changed. Every mutating call re-reads the zone's current state before writing.
That last point is unglamorous but saved us from a whole category of bugs where our cache of reality diverged from Cloudflare's reality and we quietly overwrote a customer's handcrafted record.
Problem 2 — Cache invalidation is still hard
Cloudflare's cache is effectively a giant distributed memory the customer can't see. Purging is the only way to say "please actually ask the origin again". Our job was to purge the right things at the right times without drowning in API calls.
Three rules helped:
- Events, not endpoints. We don't have code that says "when we deploy, purge everything". We emit
deploy.succeededand a cache adapter decides what to purge. That means Cloudflare logic lives in one place. - URL lists over zone purges. A full-zone purge is a hammer. It also makes every other site on that zone slower for a while. We compute the minimum URL set for each event and purge that.
- Coalesce. If three events fire in a second, we don't purge three times. We batch the URL set and purge once per 500ms window.
Every cache purge is a small tax on the whole platform. Treat them like database writes.
Problem 3 — Security posture that users understand
The WAF is the most valuable thing Cloudflare gives us and also the most likely to break a customer's site in a way they can't debug. "Why am I getting a 403 on my own admin page?" is a support ticket I never want to read again.
The tradeoff: power vs. predictability. A full WAF ruleset is powerful and unpredictable. A single "security: high / medium / low" toggle is predictable and useless.
We split the difference into three layers:
- A curated default. Sensible managed rules enabled; the obviously noisy ones off. 80% of customers never touch it.
- Guided overrides. "I need to allow
POSTto/xmlrpc.php" is a named option, not a raw rule editor. Each override carries a plain-English description and a note about what it disables. - Raw access, behind a warning. Experts get the full WAF surface — but the UI reminds them that custom rules skip the curated layer.
Support ticket volume on WAF-related issues dropped more from this than from any technical change.
The API is a contract; treat it like one
One unglamorous lesson: Cloudflare's API is excellent, but it's a contract someone else owns. When it changes — and it does — you want a single file to edit.
Our Cloudflare adapter does three things and nothing else:
- Maps our concepts (site, domain, record) to Cloudflare's concepts (zone, record).
- Handles retries, rate limits, and idempotency keys.
- Emits normalized events the rest of the system can listen to.
The rest of the codebase doesn't know Cloudflare exists. If tomorrow we switched to another edge provider for a customer, the change would be in that file plus its tests, and nowhere else.
The unified panel, finally
When we first demoed the integration, the feedback we got most often was: "wait, that's it?". Which is exactly what good plumbing should feel like. The DNS record lives next to the site it's for. The cache purge happens when you'd expect it to. The WAF has an honest toggle with honest consequences.
The best integration is the one customers stop thinking about.
That's the goal. And you only get there by picking fights with the easy abstraction early, before the shortcuts harden into the shape of the product.