Skip to content

How Micropage serves thousands of custom domains with Cloudflare for SaaS

“Bring your own domain” sounds like a checkbox. In practice it’s the part of a hosting product most likely to page you at 2am: certificate issuance, renewal, routing, and the long tail of DNS misconfigurations your customers will invent. Here’s how Micropage does it, and why we made the choices we did.

The shape of the problem

Every published Micropage site is a static build on Cloudflare Pages. We deliberately run one Pages project per customer site — cleaner blast radius, simpler rollback, per-customer CI. That decision is what makes custom domains interesting, because Pages caps how many custom domains you can attach to a single project. Attaching thousands of customer domains to one big project was never going to scale.

Custom Hostnames + a fallback Worker

Instead of attaching domains to Pages, we terminate them at the zone with Cloudflare for SaaS:

  1. A customer points a CNAME at cname.micropage.sh. (Not at their {slug}.micropage.sh subdomain — that’s Pages-attached and would route into Pages and bypass everything below.)
  2. We register their hostname as a Custom Hostname on the micropage.sh zone and issue a certificate per hostname, validated over HTTP DCV rather than TXT. No wildcard cert, no per-customer Pages project sitting against a tier limit.
  3. A fallback-origin Worker, bound to */* on the zone, reads the incoming Host header, maps it to a project slug via KV, and proxies to that customer’s {slug}.pages.dev.

The Worker is the router; Custom Hostnames is the TLS layer; Pages is the origin. Each does one job.

visitor → https://go.customer.com
        → CF edge (Custom Hostname cert, HTTP-DCV issued)
        → Worker on */*  (Host → slug via KV)
        → {slug}.pages.dev  (that customer's static build)

Our own subdomains are protected with dashboard-managed exclusion routes (*.micropage.sh/* and micropage.sh/* → Worker: None) so the catch-all never swallows traffic it shouldn’t.

The tradeoffs we accepted

  • Apex domains aren’t supported. CF for SaaS + the CNAME model means customers point a subdomain (www., go., hello.). We reject apex hostnames at the edge rather than fake it.
  • HTTP DCV over TXT. It’s a smoother first-run for non-technical customers — no waiting on TXT propagation — at the cost of the domain needing to serve the validation path.
  • One Pages project per site. More projects to manage, but every customer site is independently deployable and rollback-able.

Why not just one big multi-tenant origin?

We could have served every customer site from a single app that switches on Host. But then a bad deploy is everyone’s bad deploy, and rollback is all-or-nothing. Per-customer Pages projects keep failures contained. The Worker + Custom Hostnames layer is what lets us have both isolation and thousands of hostnames on one zone.

If you’re building something similar, the one-liner is: let Cloudflare for SaaS own TLS and hostname identity, let a Worker own routing, and keep your origins boring and isolated.

Custom domains are included on paid plans — try it.