Skip to main content

Custom Domains & SSL

Learning Focus

By the end of this module you will be able to configure a custom domain with automatic SSL on Cloudflare Pages — including both Cloudflare-managed DNS and external registrar scenarios.

Overview​

Every Cloudflare Pages project gets a free *.pages.dev subdomain. Custom domains let you serve your site at domains like docs.yourdomain.com with:

  • Automatic SSL — certificate provisioned within ~60 seconds
  • Unlimited custom domains per project
  • Automatic HTTP→HTTPS redirect
  • Global CDN — same as the default pages.dev URL

Scenario 1: Domain DNS Managed by Cloudflare (Easiest)​

This is the recommended setup and the one tested in the author's workflow. If your domain's nameservers point to Cloudflare, setup is automatic.

Step 1 — Add Domain in Dashboard​

  1. Go to Workers & Pages → your project → Custom domains
  2. Click Set up a custom domain
  3. Enter your subdomain: docs.yourdomain.com
  4. Click Continue
  5. Cloudflare shows the DNS record it will create:
    Type: CNAME
    Name: docs
    Target: my-docs.pages.dev
    Proxy: ✅ Proxied (orange cloud)
  6. Click Activate domain

Cloudflare automatically:

  • Creates the CNAME DNS record
  • Issues a TLS certificate
  • Configures HTTP→HTTPS redirect
  • Enables All Cloudflare security features (DDoS, WAF, etc.)

Step 2 — Verify​

Verify domain is active
# Check DNS propagation
dig docs.yourdomain.com CNAME

# Should return:
# docs.yourdomain.com. CNAME my-docs.pages.dev.

# Check HTTPS
curl -I https://docs.yourdomain.com

# Should return:
# HTTP/2 200
# server: cloudflare

Scenario 2: Apex Domain (yourdomain.com)​

Apex domains (bare domain, no subdomain) cannot use CNAME records. Cloudflare handles this automatically if DNS is managed by Cloudflare:

Type: CNAME (Cloudflare's DNS supports CNAME at apex via CNAME flattening)
Name: @ (apex)
Target: my-docs.pages.dev
Proxy: ✅ Proxied

Cloudflare automatically flattens the CNAME to A records — this is unique to Cloudflare's DNS implementation and not available at most other registrars.

www → Root Redirect​

If you use the apex domain (yourdomain.com), add a www redirect:

  1. Add a CNAME for www:
    Type: CNAME
    Name: www
    Target: my-docs.pages.dev
  2. Add a Cloudflare Redirect Rule (or use Pages _redirects file):
/_redirects file for www to apex redirect
# In your Docusaurus static/ directory, create static/_redirects:
https://www.yourdomain.com/* https://yourdomain.com/:splat 301

Scenario 3: External DNS (Not Managed by Cloudflare)​

If your domain is at another registrar (Namecheap, GoDaddy, Route 53, etc.):

Subdomain (docs.yourdomain.com)​

Add this CNAME record at your registrar:

Host: docs
Type: CNAME
Value: my-docs.pages.dev
TTL: 3600 (or Auto)

Then in Cloudflare Pages:

  1. Add the custom domain docs.yourdomain.com
  2. Cloudflare will attempt to verify ownership via DNS TXT record
  3. Once verified, SSL is provisioned automatically

Apex Domain with External DNS​

External registrars usually don't support CNAME at apex. Options:

Option A: Use A records (Cloudflare's IP ranges) — Not supported for Pages (Pages has no static IPs).

Option B: Migrate DNS to Cloudflare — Recommended. Transfer nameservers to Cloudflare (free):

  1. Add your domain to Cloudflare (free plan)
  2. Update nameservers at your registrar to Cloudflare's
  3. Then follow Scenario 1 above

Option C: Use www subdomain as canonical URL — Set www.yourdomain.com as your Pages domain, then redirect apex to www at your registrar level.


Managing Custom Domains via API​

List Domains​

GET all domains for a project
curl "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/domains" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
| jq '.result[] | {name, status, ssl}'

Add Domain​

POST add a custom domain
curl -X POST \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/domains" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"name": "docs.yourdomain.com"}' | jq .

Delete Domain​

DELETE a custom domain
curl -X DELETE \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/domains/docs.yourdomain.com" \
-H "Authorization: Bearer ${CF_API_TOKEN}"

SSL Configuration​

SSL is Always Automatic​

Cloudflare Pages provisions SSL via:

  • Cloudflare Universal SSL for *.pages.dev domains
  • Cloudflare SSL for custom domains (DV certificate, 90-day rotation handled automatically)

You do not need to configure Let's Encrypt, Certbot, or any ACME client. The certificate is:

  • Free
  • Automatically renewed
  • Valid for your domain + all subdomains

SSL Mode​

When your domain DNS is managed by Cloudflare:

  • SSL mode is set to Full (strict) by default — Cloudflare validates the certificate between itself and Pages.
  • This is the highest security mode and requires no configuration.

Check SSL Status via API​

Check domain SSL status
curl "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/domains/docs.yourdomain.com" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
| jq '.result | {name, status, ssl: .ssl}'

Redirects with _redirects File​

Cloudflare Pages supports Netlify-compatible _redirects files for URL redirects and rewrites.

Place _redirects in your output directory (for Docusaurus: static/_redirects → copied to build/_redirects).

static/_redirects — common patterns for Docusaurus
# Redirect old URL structure to new
/guide /docs/intro 301

# Redirect www to apex
https://www.yourdomain.com/* https://yourdomain.com/:splat 301

# Redirect old docs prefix
/docs/* /:splat 301

# Rewrite API calls (proxy to Worker without redirect)
/api/* https://api.yourdomain.com/:splat 200

# SPA fallback (not needed for Docusaurus — it generates real HTML files)
/* /index.html 200

Syntax: <source> <destination> <status>

_headers File​

Control response headers per path:

static/_headers — security headers
/*
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()

/docs/*
Cache-Control: public, max-age=0, must-revalidate

HSTS and Security​

Cloudflare Pages automatically adds:

  • Strict-Transport-Security header
  • Automatic HTTPS redirect

You can enhance security headers via the _headers file or Cloudflare Transform Rules (in the zone settings).


Troubleshooting Custom Domains​

Domain Shows "Initializing" for More Than 5 Minutes​

# Check if DNS record exists
dig docs.yourdomain.com CNAME

# If record doesn't exist:
# - For Cloudflare DNS: check if record was auto-created in DNS tab
# - For external DNS: add the CNAME record manually at your registrar

SSL Certificate Takes Too Long​

# Check domain status via API
curl "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/domains/docs.yourdomain.com" \
-H "Authorization: Bearer ${CF_API_TOKEN}" | jq .

# Common fix: wait 5-10 minutes after DNS propagation
# If "blocked" status: check for CNAME conflicts (existing A or AAAA record at same name)

Domain Shows "Blocked" Status​

Causes:

  1. An A or AAAA record already exists for the same hostname — delete it
  2. Ownership verification failed — re-add the domain and follow verification steps

Custom Domain Works But Shows Wrong Content​

# Clear Cloudflare cache
curl -X POST \
"https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/purge_cache" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"purge_everything": true}'

Key Takeaways​

  • Cloudflare Pages supports unlimited custom domains per project, free SSL included.
  • If your DNS is Cloudflare-managed, adding a custom domain takes ~60 seconds.
  • Use the _redirects file (in static/ for Docusaurus) for URL redirects — Netlify-compatible syntax.
  • Use the _headers file for custom response headers (CSP, HSTS, Cache-Control).
  • Apex domains work with Cloudflare DNS via CNAME flattening.

What's Next​