Docs

This page isn't translated yet

next

Zones and domains

Zone resolution and multiple locale domains.

On this page

Every Cloudflare call goes to a zone. This page covers how Systhema finds the zone for your site, and what changes when the site publishes through several domains.

Zone resolutionLink to this section

An explicit zoneId is used directly. Otherwise the zone is resolved from siteUrl's host by querying Cloudflare for an active zone matching that host, walking up subdomains until a match is found — www.blog.example.com → blog.example.com → example.com (a bare TLD is never tried). A resolved zone (id, name, plan, account id — the latter only ever used server-side to build the dashboardUrl deep-link) is cached for an hour in payload.kv. Not every failure to resolve is treated the same: a genuine "no matching active zone" result (Cloudflare responded, nothing matched) is a real null and is cached for only 60 seconds — not the full hour — so fixing DNS/zone activation is reflected within a minute instead of staying "Not connected" for up to an hour. An API/network failure (unreachable credentials, a timeout) is never persisted as that null — it falls back to serving a still-fresh cached zone if one exists, or otherwise surfaces as "not connected" without writing a new cache entry, so a transient outage can't get stuck caching a false negative.

Multiple domains (locale domains)Link to this section

A domain-routed multilingual site can publish through more than one registrable domain — say example.eu carrying en, fr and nl under path prefixes, and example.hu carrying hu on its own domain. Each registrable domain is a separate Cloudflare zone, and Cloudflare rejects a purge for a URL outside the zone it was sent to.

Purging handles that for you, with no extra configuration:

  • Per-URL purges (publishing a page, editing a component or form, redirects, uploads) group the affected URLs by host and issue one purge per owning zone. A Hungarian page on example.hu is purged against the example.hu zone, not dropped because the primary zone is example.eu.
  • Whole-site purges (general settings, header/footer, "Revalidate site", the admin bar's "Clear all caches") purge every zone the site publishes through — the canonical site host plus every configured locale domain.
  • Hosts sharing a zone (example.eu and shop.example.eu) are deduplicated, so one zone is never purged twice.
  • A locale domain that isn't on Cloudflare at all is a supported setup, not an error: those pages are still revalidated in Next.js, and a warning names the hosts whose edge cache was left alone — the purge is never silently skipped.

An explicit zoneId pins the primary domain's zone only; the other domains are still resolved by name, so pinning one zone never sends another domain's purge to the wrong place.

One credential, many zones. There is a single apiToken (or key + email) for the whole module — every zone is reached with the same credential, so it must be able to see and purge all of them.

A scoped API token handles this fine; the only thing to get right is the token's Zone Resources, which defaults to a single zone:

Your domainsWhat to create
All in one Cloudflare accountAPI token, Zone Resources → Include → All zones from an account (or list each zone)
Spread across several accountsA user API token (Zone Resources → All zones), or the legacy Global API Key

The Global API Key works in every case because it carries full account access — which is also why it is the fallback rather than the recommendation. Prefer a token whenever one can cover your zones, so the credential your CMS holds can be revoked and re-scoped without touching anything else.

When a domain's zone can't be resolved — a too-narrow token, a domain not on Cloudflare, a zone in an account the token can't see — those pages are still revalidated in Next.js and a warning names the hosts whose edge cache was skipped:

[systhema cloudflare] auto-purge skipped for hosts with no active Cloudflare zone
  { hosts: ['example.hu'] }

Managing every domain from the admin pageLink to this section

When more than one domain resolves to a Cloudflare zone, the Cloudflare admin page grows a domain selector above the connection-status panel, and everything below it — the Cache, Security and Speed tabs, and "Optimize for Systhema" — reads and writes the selected domain's zone rather than the primary one. Optimize is labelled with the domain it will act on, so a one-click bundle is never ambiguous about where it lands.

The selector renders only when at least two distinct zones resolve. A single-domain site — however many locales it serves — gets exactly the page it had before: no selector, no badges, no apply-to-all button. The selection is deliberately not remembered between visits: the page always opens on the primary domain, because a remembered write target is how someone returns weeks later, recognises the toggles, and edits the wrong domain believing it is the primary one.

The connection-status panel expands to list every publishing host with its own zone name and plan, or "Not on Cloudflare" for a domain that never resolved — so a too-narrow token surfaces as a named domain rather than one that silently isn't there.

"Differs" badges. Every setting row is compared across the resolved domains and carries a badge when they disagree, so drift is visible without opening each domain in turn; the selector shows a per-domain count ("2 differ") for the same reason.

Badges cover the settings this page renders a control for — security_level, waf, always_online, automatic_https_rewrites, mirage and polish — so a count always matches the number of badges you can actually find and act on. Apply-to-all syncs a wider set, adding the performance settings the Optimize bundle owns (cache_level, browser_cache_ttl, ipv6, websockets, ip_geolocation, email_obfuscation, server_side_exclude, hotlink_protection, rocket_loader); those have no toggle here, so they are disclosed in the apply-to-all diff rather than badged. Neither set is the raw zone-settings response — Cloudflare exposes around fifty settings, most of which this page does not manage and whose per-domain differences would be noise.

A setting another domain reports as not editable, or that its plan doesn't offer (Mirage/Polish below Business), reads as incomparable rather than as drift. Those are permanent states, and a "differs" badge that can never be cleared is noise, not information. A domain whose settings couldn't be read at all makes the comparison unknown, not "matches": no badges appear on the rows, and that domain is flagged with an error marker in the selector and the connection panel so the gap is attributable.

The selected domain's settings load first — the single-domain fast path is unchanged — and the other domains load in the background. Badges simply appear as those reads land; nothing on the page waits for them.

Apply to all domains. A whole-page action (not per-tab) that copies the selected domain's comparable settings onto every other resolved domain. It always shows a diff first: clicking it runs a dry run and opens a confirmation listing, per domain, exactly what would change and from what to what (example.hu: always_online off → on). If nothing differs, it says so instead of offering a no-op write.

A domain whose settings could not be read is excluded, not applied to blind — it's named in the confirmation as excluded, and the apply skips it. Systhema will not overwrite a zone whose current state it doesn't know.

Confirming runs the real apply and reports a per-domain applied / skipped / failed breakdown, the same shape as the Optimize button's results. Partial success is normal: domains on different Cloudflare plans genuinely can't hold the same settings, so a Business-only value skipped on a Free zone (or a setting the token can't edit there) is reported as skipped while everything else applies.

Development mode is never synced. development_mode is deliberately outside the compared and synced set. It's a temporary, per-zone debugging state that Cloudflare reverts on its own after three hours — and it's the one setting where copying the source domain's value could switch a live production domain into development mode, bypassing its cache entirely, as a side effect of an action the user understood to be about cache levels and security headers. Turn it on per domain, from the Cache tab, with that domain selected.

The dashboard widgets stay primary-zone only. This is an explicit non-goal, not an oversight. The four Cloudflare widgets always report the zone resolved from siteUrl; there is no per-domain analytics view, since that would mean threading a domain dimension through every widget and the shared range selector for a number that is rarely compared side by side. For a second domain's traffic, use Cloudflare's own dashboard — the external-link icon on each widget card deep-links straight into it.