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