A few years ago a SaaS customer told me their biggest enterprise deal was stuck because they couldn't put their help center on help.customer.com with a valid certificate. It sounds trivial until you have 2,000 customers all asking for the same thing. That one request for a custom domain SaaS setup turned into a six-week project of DNS tickets, manual CSR generation, and angry emails — and it's exactly why vanity domains need automation from day one.
Why "Just Add a CNAME" Falls Apart at Scale
On paper, customer domains CDN sounds simple. Your customer creates a CNAME from docs.customer.com to something like cname.your-saas.com, you add it to your CDN config, and traffic flows. I've used that model with small SaaS products and it works for the first 20 or 30 domains. The problems start when you hit a few hundred. Every new hostname is a new edge configuration, a new certificate, and a new place where caching can go wrong if you don't isolate tenants properly.
What most teams don't anticipate is the operational drag of hostname onboarding. You can't just accept any Host header and proxy it — that's how you get cache poisoning and cross-tenant data leaks. You need a validation step to prove the customer actually owns the hostname, a pending state for domains that haven't completed DNS yet, and a way to map that hostname to the right tenant's origin and cache namespace. When we didn't do this at a previous company, one customer's misconfigured wildcard ended up serving another customer's logged-in dashboard from cache for 12 minutes. That's the kind of mistake that forces you to rethink your CDN Multi-Tenant Architecture: Isolating Customer Traffic While Sharing Infrastructure before you add another vanity domain.
The pattern that finally worked for us was treating custom domains as a first-class resource with its own lifecycle, not just a field in a database. We give each customer a unique target they must CNAME to, we validate ownership, and only then do we push the hostname to the edge. Here's the simplified provisioning flow we now run behind an internal API:
// POST /api/v1/custom-domains
// Creates a domain in "pending" state and returns validation details
{
"customer_id": "cust_8f3a2b",
"vanity_hostname": "docs.acme.co",
"validation": {
"method": "cname",
"expected_target": "cust-8f3a2b.saas-edge.example.net",
"status": "pending_dns"
},
"cdn_config": {
"origin": "tenant-8f3a2b.origins.internal",
"cache_key_prefix": "tenant:8f3a2b",
"created_at": "2025-11-02T14:20:00Z"
}
}
// Verification worker checks DNS every 60s:
// dig CNAME docs.acme.co -> cust-8f3a2b.saas-edge.example.net ?
// If yes, promote to "active" and trigger SSL automation
SSL Automation That Doesn't Require a Spreadsheet and a Prayer
I still remember maintaining a spreadsheet of 400 customer certificates with expiration dates highlighted in red. We'd get Let's Encrypt emails at 2 AM, manually run certbot, and upload certs through a CDN dashboard. We missed one renewal for a financial services customer and their status page went down with a browser warning for four hours. I don't manage certs that way anymore, and you shouldn't either.
Modern SSL automation for vanity domains relies on ACME and the CDN's ability to answer challenges on behalf of the hostname. For most SaaS use cases, you have two practical options: HTTP-01 and DNS-01. HTTP-01 is simpler if your CDN can serve the `/.well-known/acme-challenge/` token on the customer's hostname before the certificate exists — which requires your edge to handle that path specially. DNS-01 is more powerful for wildcard or apex domains but means you need to create a TXT record or have the customer delegate validation. The HTTP Working Group Specifications are useful here for understanding how challenge routing should behave without breaking normal cache semantics.
We standardized on HTTP-01 with automatic fallback to DNS-01 for customers who can't use CNAME (like those wanting an apex domain). The CDN handles certificate issuance, renewal, and deployment without us touching private keys. If you're building this yourself instead of using a provider with built-in managed certs, tools like cert-manager, Caddy, or a direct ACME client integrated with your edge do the heavy lifting. We also tie this directly to CDN Tenant Isolation Patterns: Cache Separation, Rate Limits, and Access Controls so a single failing challenge doesn't block renewals for everyone else. This worker is essentially what runs every night for us:
#!/usr/bin/env bash
# nightly acme renewal check for active vanity domains
# Uses DNS-01 via Cloudflare API for wildcard fallback
DOMAINS=$(psql -t -c "SELECT hostname FROM custom_domains WHERE status='active'")
for domain in $DOMAINS; do
# Check expiry within 20 days
expiry=$(check_cert_expiry "$domain")
if [[ $expiry -lt 20 ]]; then
echo "Renewing $domain (expires in ${expiry}d)"
# caddy or lego will handle HTTP-01 automatically if CNAME is valid
# --dns flag only used if http validation fails twice
lego --email="[email protected]" \
--dns cloudflare \
--domains="$domain" \
--accept-tos run
# Upload issued cert to CDN edge via API
upload_to_edge "$domain" "/etc/lego/certificates/${domain}.crt"
fi
done
Choosing a DNS Strategy Your Support Team Won't Regret
Customers will mess up DNS. That's not a criticism, it's a fact I've learned after watching enterprise IT teams take three weeks to add a single CNAME. Your job is to make the correct setup obvious and the incorrect setup impossible to miss. We used to ask for a generic CNAME to a shared target, but that made it hard to know who owned what. Now we issue a unique, per-customer target like `cust-8f3a2b.saas-edge.example.net` and require that exact value. It's easy to validate, it prevents takeovers, and it gives you a clean way to revoke a domain.
You've got three common patterns, and each has tradeoffs for provisioning time and support burden. Simple CNAME is great for subdomains like docs.customer.com, but it doesn't work for apex domains like customer.com where CNAME isn't allowed. For apex, you need either an ALIAS/ANAME record if their DNS provider supports it, or you ask for delegation via NS records or a TXT-based ownership proof. For White-Label CDN Delivery: Custom Branding for SaaS Customer Domains you'll also want to think about how the hostname appears in TLS handshakes and whether you need dedicated IPs for customers with strict firewall rules. Guidance from web.dev Articles on custom domain performance helped us tune our TLS and HSTS defaults for these branded hostnames.
Don't promise instant activation. Even with automation, DNS propagation and certificate issuance take time. We show customers a clear status page — pending DNS, validating, issuing certificate, active — and we've cut support tickets by about 60% since adding it. Here's how the options compare from our last 12 months handling roughly 4,300 vanity domains:
| Validation Method | Avg. Time to Active | Renewal Failure Rate | Best For |
|---|---|---|---|
| CNAME + HTTP-01 Challenge | 2.4 minutes | 0.3% | Subdomains (docs.customer.com) |
| TXT + DNS-01 Challenge | 8.7 minutes | 1.1% | Wildcard & apex domains |
| Delegated Subdomain (NS) | 14.2 minutes | 0.6% | Enterprise customers needing full control |
| ALIAS / ANAME at Apex | 4.1 minutes | 0.4% | Apex domains on modern DNS providers |
From CNAME to Live: Building the Zero-Touch Provisioning Pipeline
Here’s where most teams finally get it right: you stop treating custom domains as a support ticket and start treating them as an API workflow. The ideal state is a customer types app.customer.com into your dashboard, you give them a single validation target, and the rest happens without anyone on your team touching DNS or certs. We’ve seen SaaS platforms cut onboarding time from 2-3 days (and 4-5 back-and-forth emails) to under 90 seconds by automating this loop.
The flow we run at scale looks like this: 1) Your app calls the CDN control plane to create a custom hostname. 2) The CDN returns the required verification — either a TXT record for domain ownership and a CNAME target like customer SaaS-id.cdn-provider.net. 3) Your backend polls for validation status and provisions the cert via ACME (Let’s Encrypt or ZeroSSL) with automatic renewal at 30 days, not 89. Don’t wait until day 88, you’ll get rate-limited and paged at 2am. Store that status and surface it in your UI — “Pending Validation,” “SSL Issuing,” “Live” — so support isn’t guessing. If you’re already tracking usage for billing, tie the domain creation event to your metering pipeline early; see API Metering at CDN Edge: Real-Time Usage Tracking for SaaS Billing for how we do it without double-counting.
The code isn’t complicated, but the idempotency and error handling is. You must handle the three failure modes that account for ~95% of tickets: customer typo’d the CNAME, they CNAME’d to the apex without flattening, or their corporate DNS takes 45 minutes to propagate and they spam your “verify” button 12 times. Queue the verification job, debounce it, and lock the hostname to the tenant ID.
// Automated vanity domain provisioning with Cloudflare for SaaS / ACME
// POST /api/v1/custom-hostnames - idempotent by customerId + hostname
async function provisionVanityDomain(customerId, hostname) {
// 1. Create hostname at edge - Cloudflare, Fastly,fly.io all have this
const res = await cdn.createCustomHostname({
hostname: hostname, // e.g., 'app.acme.co'
ssl: {
method: 'txt', // Use TXT DCV, not HTTP - works behind WAFs
type: 'dv',
settings: { min_tls_version: '1.2', http2: true }
}
});
// 2. Return validation instructions to your frontend
// res.ownership_verification = { type: 'txt', name: '_cf-custom-hostname.acme.co', value: '...' }
// res.ssl.validation_records[0].txt_name / txt_value
await db.customDomains.upsert({
customerId,
hostname,
cdnId: res.id,
status: 'pending_validation',
validation: res.ownership_verification,
cnameTarget: 'saas.edge.cdn-provider.net'
});
// 3. Background worker polls every 30s for 15m, then backs off to 5m
queue.enqueue('verify-custom-hostname', { cdnId: res.id }, { retries: 48 });
return res.ownership_verification;
}
Advanced Patterns, Edge Cases, and 3am Troubleshooting
Once you’re past 500-1,000 vanity domains, the “happy path” stops being your problem. It’s the edge cases. Apex domains are the first landmine. You can’t CNAME an apex — you can tell a customer 100 times, they’ll still try to CNAME acme.co. You have two sane options: support ALIAS/ANAME if your DNS provider allows it, or more commonly, require they use www.acme.co and forward the apex with a 301 at their registrar. We explicitly detect apex hostnames in the API and return a 400 with a human-readable fix — it cuts apex-related tickets by about 80%.
Wildcards and path-based tenants are next. Don’t offer *.customer.com on day one unless you absolutely need it. Wildcard cert issuance requires DNS-01 validation, which means you need programmatic control of the customer’s DNS or a delegated _acme-challenge subdomain — complex and fragile. Stick to single hostname SAN certs; they’re faster (issuance in 5-15 seconds vs 60-90s for wildcards) and they keep your blast radius small if a renewal fails. And please, isolate vanity domains at the edge. Use a dedicated hostname routing + tenant-aware cache key. We’ve seen one misconfigured Vary: Host leak cached data from customer-a.yoursaas.com to customer-b.com. Not a call you want to make.
Finally, observability is non-negotiable. You need per-hostname metrics from the moment the domain goes live. Don’t aggregate all vanity traffic into one bucket — you’ll have no idea who’s spiking origin or costing you $3k in egress. Hook your edge logs into per-customer analytics so you can alert when a single vanity domain hits 5x its 7-day median or starts throwing 45% 525 SSL handshake errors. That’s usually a broken origin cert or a customer who deleted their CNAME. For a deeper setup, check CDN Usage Tracking for SaaS: Per-Customer Bandwidth and Request Analytics and if your enterprise customers demand vanity domains with their own IdP, you’ll need Enterprise SSO Integration with CDN: SAML, OIDC, and Custom Identity Providers before you promise login.customer.com works with their Okta.
Frequently Asked Questions
Should I use Cloudflare for SaaS, AWS CloudFront, or roll my own with Let's Encrypt?
At under ~200 domains, rolling your own with greenlock.js or cert-manager on Kubernetes works. Past that, buy it vs build it. Cloudflare for SaaS ($2-5/hostname/mo) or Fastly’s managed TLS handles validation, renewal, and OCSP stapling for 10k+ hostnames without you hitting Let’s Encrypt’s 50 certs per domain per week rate limits. CloudFront + ACM is cheaper but slower to provision (15-20 mins) and forces you into AWS plumbing.
Do I need to ask customers for a TXT record AND a CNAME?
Yes, if you want it done right. The TXT (or _cf-custom-hostname) proves ownership for SSL issuance, the CNAME routes traffic. Some providers combine them with a single CNAME pointing to a validation-prefixed target, but we prefer two explicit records — it’s clearer for customers and lets you validate ownership before you waste time on routing. Always set TXT TTL to 60-300s so retries aren’t cached for an hour.
How do I handle SSL renewal without downtime?
Renew at 30 days for a 90-day cert, not at expiry. Use DCV via CNAME’d validation records that stay in place permanently, not one-time TXT records the customer deletes. That way renewals are fully automatic and don’t require customer action. Keep the old cert serving until the new one is validated and deployed to all 250+ edge PoPs — most managed providers do this atomically.
What if an enterprise customer wants their vanity domain behind their own WAF or proxy?
That’s an “orange-to-orange” double CDN problem and you should try to avoid it. It breaks automatic HTTPS and adds 30-80ms of latency. If they insist, have them set their proxy to DNS-only / grey-cloud for your hostname and do all caching/WAF at your edge. Document it. Otherwise you’ll be debugging why their Cloudflare Bot Fight Mode is blocking your health checks forever.