Operations, Troubleshooting & Reference
By the end of this module you will have a complete operational toolkit for running Cloudflare Pages in production — including incident response, rollback, cache management, and a comprehensive CLI/API quick-reference.
Production Operational Runbook
Checking Site Status
# 1. Check the live site responds
curl -I https://docs.yourdomain.com
# Expected: HTTP/2 200, server: cloudflare
# 2. Check certificate validity
curl -I https://docs.yourdomain.com 2>&1 | grep -i "SSL\|TLS\|certificate"
# 3. Check which deployment is active
curl "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
| jq '.result.latest_deployment | {url, created_on, environment}'
# 4. Check the deployment status
curl "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/deployments?per_page=5" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
| jq '.result[] | {id, url, environment, created_on, latest_stage}'
Rollback Procedure
# Step 1: List recent deployments
curl -s "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/deployments" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
| jq '.result[] | "\(.id) | \(.created_on) | \(.latest_stage.status)"' -r
# Step 2: Pick the last known-good deployment ID, then rollback:
GOOD_DEPLOY_ID="previous-good-deployment-id"
curl -X POST \
"https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/deployments/${GOOD_DEPLOY_ID}/rollback" \
-H "Authorization: Bearer ${CF_API_TOKEN}" | jq .
# ✅ The old deployment is now live — no rebuild required (~5-10 seconds)
Dashboard Rollback:
- Workers & Pages → your-project → Deployments
- Find the deployment to restore
- Click the ⋯ menu → Rollback to this deployment
- Confirm
Cache Purge
# Get your Zone ID from Cloudflare dashboard (for the custom domain's zone)
ZONE_ID="your-zone-id"
# Purge everything (use during incidents)
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}'
# Purge specific URLs (less disruptive)
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 '{
"files": [
"https://docs.yourdomain.com/",
"https://docs.yourdomain.com/docs/intro",
"https://docs.yourdomain.com/docs/intro/"
]
}'
Troubleshooting Matrix
Build Failures
| Error | Cause | Fix |
|---|---|---|
Cannot find module 'X' | Missing dependency | npm install X && git add package*.json && git push |
command not found: docusaurus | @docusaurus/core not installed | Ensure package.json has Docusaurus as dependency |
Build timeout (20 minutes) | Site too large / memory leak | Set NODE_OPTIONS=--max-old-space-size=4096 |
ENOSPC: no space left on device | Too many files in output | Audit static/ for accidental large files |
Error: baseUrl mismatch | Wrong baseUrl in config | Set baseUrl: '/' in docusaurus.config.js |
yarn.lock out of date | Lock file conflict | Run yarn install locally and commit updated lock |
Node version mismatch | Wrong Node for Docusaurus | Set NODE_VERSION=20 env var |
Deployment Issues
| Symptom | Cause | Fix |
|---|---|---|
| Build succeeds but site 404s | Wrong output directory | Verify it's build (not dist, public, out) |
| CSS/JS assets 404 | Wrong baseUrl | Set baseUrl: '/' |
| Old content showing | CDN cache | Purge cache (see above) |
| Custom domain stuck at "initializing" | DNS not propagated | Wait 5-10min, or check with dig |
| HTTPS shows warning | SSL race condition | Wait up to 15 min for cert issuance |
| Functions not running | functions/ in wrong location | Must be at repo root, not inside Docusaurus dir |
DNS Issues
# Check CNAME record
dig docs.yourdomain.com CNAME
# Expected:
# docs.yourdomain.com. 300 IN CNAME my-docs.pages.dev.
# Check propagation globally
# https://dnschecker.org/#CNAME/docs.yourdomain.com
# Check SSL certificate
echo | openssl s_client -connect docs.yourdomain.com:443 2>/dev/null | openssl x509 -noout -dates
Pages Functions Debugging
# Local debugging
wrangler pages dev build --log-level debug
# Production logs (real-time)
wrangler pages deployment tail \
--project-name my-docs \
--format pretty \
--status error # Show only errors
# Get logs from a specific deployment
DEPLOYMENT_ID="your-deployment-id"
curl "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/deployments/${DEPLOYMENT_ID}/history/logs" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
| jq '.result.logs[] | "\(.ts) \(.line)"' -r
Performance Optimization
Docusaurus Build Optimization for Pages
const config = {
// Enable trailing slash for better CDN cache hit rates
trailingSlash: true,
// Minify HTML in production
customFields: {
minifyHtml: true,
},
// Image optimization
themeConfig: {
image: 'img/social-card.jpg', // OG image for social sharing
},
};
Cache Control Headers
# Hashed assets (Docusaurus adds hashes to JS/CSS filenames)
/assets/*
Cache-Control: public, max-age=31536000, immutable
# HTML pages — always revalidate
/*.html
Cache-Control: public, max-age=0, must-revalidate
# Root and clean URLs
/
Cache-Control: public, max-age=0, must-revalidate
Limits & Quotas Reference
Cloudflare Pages Limits
| Resource | Free | Pro | Business |
|---|---|---|---|
| Projects | Unlimited | Unlimited | Unlimited |
| Builds/month | 500 | 5,000 | 5,000 |
| Concurrent builds | 1 | 5 | 5 |
| Build timeout | 20 min | 20 min | 20 min |
| Custom domains/project | Unlimited | Unlimited | Unlimited |
| Bandwidth | Unlimited | Unlimited | Unlimited |
| Max file size | 25 MB | 25 MB | 25 MB |
| Files per deployment | 20,000 | 20,000 | 20,000 |
| Preview deployments | Unlimited | Unlimited | Unlimited |
Pages Functions Limits
| Resource | Free | Paid (Workers Paid) |
|---|---|---|
| Requests/day | 100,000 | 10M included |
| CPU time/invocation | 10ms | 30s |
| Memory | 128 MB | 128 MB |
| Subrequests | 50 | 1,000 |
| Script size | 1 MB | 10 MB |
API Rate Limits
| Action | Limit |
|---|---|
| API requests | 1,200/min |
| Deployments/day | 1,000 |
| Domains per project | 100 |
CLI Quick Reference
# ── Authentication ─────────────────────────────────────────
wrangler login # OAuth browser login
wrangler logout # Remove credentials
wrangler whoami # Show current user and accounts
# ── Project Management ──────────────────────────────────────
wrangler pages project list # List all projects
wrangler pages project create <name> # Create new project
wrangler pages project delete <name> # Delete project
# ── Deployments ─────────────────────────────────────────────
wrangler pages deploy <dir> # Deploy directory
--project-name <name> # Target project (required)
--branch <branch> # Branch for this deploy
--commit-message <msg> # Label in dashboard
--commit-dirty=true # Skip dirty repo check
wrangler pages deployment list # List deployments
--project-name <name> # For specific project
wrangler pages deployment view <id> # View deployment details
--project-name <name>
# ── Local Dev ───────────────────────────────────────────────
wrangler pages dev <dir|url> # Start local dev server
--port <port> # Default: 8788
--live-reload # Enable live reload
--log-level debug # Verbose output
# ── Secrets & Env ───────────────────────────────────────────
wrangler pages secret put <name> # Set a secret
--project-name <name>
--env production|preview
wrangler pages secret list # List secrets
--project-name <name>
--env production|preview
wrangler pages secret delete <name> # Delete a secret
--project-name <name>
--env production|preview
# ── Logs ────────────────────────────────────────────────────
wrangler pages deployment tail # Stream live logs
--project-name <name>
--format pretty|json
--status ok|error|canceled
--env production|preview
API Quick Reference
BASE="https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects"
AUTH="-H 'Authorization: Bearer ${CF_API_TOKEN}'"
# ── Projects ────────────────────────────────────────────────
GET ${BASE} # List all projects
GET ${BASE}/${PROJECT} # Get project details
POST ${BASE} # Create project
PATCH ${BASE}/${PROJECT} # Update project settings
DELETE ${BASE}/${PROJECT} # Delete project
# ── Deployments ─────────────────────────────────────────────
GET ${BASE}/${PROJECT}/deployments # List deployments
GET ${BASE}/${PROJECT}/deployments/${ID} # Get deployment
POST ${BASE}/${PROJECT}/deployments # Trigger deployment
POST ${BASE}/${PROJECT}/deployments/${ID}/retry # Retry failed deployment
POST ${BASE}/${PROJECT}/deployments/${ID}/rollback # Rollback to deployment
DELETE ${BASE}/${PROJECT}/deployments/${ID} # Delete deployment
GET ${BASE}/${PROJECT}/deployments/${ID}/history/logs # Build logs
# ── Domains ─────────────────────────────────────────────────
GET ${BASE}/${PROJECT}/domains # List custom domains
POST ${BASE}/${PROJECT}/domains # Add custom domain
GET ${BASE}/${PROJECT}/domains/${DOMAIN} # Get domain status
DELETE ${BASE}/${PROJECT}/domains/${DOMAIN} # Remove custom domain
# ── Deploy Hooks ────────────────────────────────────────────
GET ${BASE}/${PROJECT}/deploy_hooks # List webhooks
POST ${BASE}/${PROJECT}/deploy_hooks # Create webhook
DELETE ${BASE}/${PROJECT}/deploy_hooks/${ID} # Delete webhook
Security Best Practices
- Use scoped API tokens — never use your Global API Key for automation.
- Rotate API tokens — create new tokens periodically (token management in CF dashboard).
- Protect preview deployments — use Cloudflare Access for team-only previews.
- Use encrypted secrets — mark sensitive values as encrypted in Pages settings.
- Enable branch protection — protect your main branch in GitHub to require PR reviews.
- Monitor deployments — check the deployments dashboard or set up Cloudflare email alerts.
- Audit deploy hooks — remove unused webhooks to prevent unauthorized builds.
Key Takeaways
- Rollback is instant via API or dashboard — no rebuild required.
- Cache purge via API solves most "old content" incidents.
- Build failures are almost always caused by missing dependencies, wrong output directory, or memory limits.
- DNS issues are diagnosed with
dig— most resolve within 10 minutes. - Functions logs are streamed with
wrangler pages deployment tail. - Free tier: 500 builds/month, 100k function requests/day, unlimited bandwidth.
What You've Learned
Congratulations — you've completed the Cloudflare Pages curriculum. You can now:
✅ Understand the JAMstack architecture and why it supersedes self-hosted servers
✅ Migrate a Docusaurus site from Docker/VPS to GitHub + Cloudflare Pages
✅ Use the Wrangler CLI for deployments, secrets, logs, and local development
✅ Call the Cloudflare Pages REST API for automation and scripting
✅ Configure build settings, environment variables, and framework presets
✅ Leverage preview deployments for staging and team review workflows
✅ Configure custom domains and automatic SSL
✅ Write Pages Functions (edge functions) for server-side logic
✅ Build GitHub Actions CI/CD pipelines with test gates
✅ Operate Pages in production — rollbacks, cache management, and troubleshooting