Cloudflare Pages
Deploying prestruct sites to Cloudflare Pages.
Why Cloudflare Pages?
- Free tier: Unlimited requests, sites, and bandwidth
- Edge network: 300+ data centers globally
- Zero config: Git push to deploy
- Security: Automatic HSTS, CSP, and DDoS protection
Quick Deploy
- Push your code to GitHub
- Go to Cloudflare Dashboard → Pages
- Click “Connect to Git”
- Select your repository
- Configure build settings:
- Build command:
npm run build - Build output directory:
dist - Node.js version:
18
- Build command:
- Click “Save and Deploy”
Configuration
Environment Variables
In Cloudflare Pages settings, add:
| Variable | Value |
|---|---|
PRODUCTION |
true |
NODE_VERSION |
18 |
For Proxy Support
If using the proxy feature, add these in Cloudflare:
- PRESTRUCT_TARGET_URL: Your origin URL (e.g.,
https://api.example.com) - PRESTRUCT_SECRET: Your auth secret (add as a secret, not variable)
Security Headers
Prestruct includes public/_headers with these security policies:
/*
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https:
/*
Cache-Control: public, max-age=0, must-revalidate
/assets/*
Cache-Control: public, max-age=31536000, immutable
Update the CSP connect-src to include any third-party APIs your app uses.
Redirects
Use public/_redirects for redirects:
/old-page/ /new-page/ 301
/beta / 302
Important: Don’t include /* /index.html 200 - this causes infinite redirect loops with prerendered sites.
Pretty URLs
Cloudflare Pages Pretty URLs automatically handles trailing slashes:
/about→ serves/about/index.html/about/→ serves/about/index.html
No redirect rules needed - just be consistent in your ssr.config.js route paths.
Custom Domains
- Go to Cloudflare Dashboard → Pages → Your project → Custom domains
- Click “Set up a custom domain”
- Enter your domain
- Create a CNAME record pointing to yourPages subdomain
Cloudflare handles SSL automatically.
Cache Invalidation
Automatic
Cloudflare automatically purges cache when you redeploy.
Manual
Send a cache-busting request:
curl -H "x-prestruct-refresh: your-secret" https://your-domain.com/path
Requires PRESTRUCT_SECRET to be set.
Troubleshooting
Build fails
- Check Node.js version is set to 18+
- Ensure all dependencies are in
package.json - Verify build command matches local:
npm run build
404 on all routes
- Ensure
public/_redirectsexists (even if empty) - Check build output went to
dist/
Assets not loading
- Verify
dist/containsassets/folder - Check asset paths in HTML match actual files
Infinite redirect
- Remove
/* /index.html 200from_redirects
Advanced: Cloudflare Workers
For dynamic routes that can’t be prerendered, use Cloudflare Pages Functions:
// functions/[[path]].js
export async function onRequestGet({ params }) {
const path = params.path?.join('/') || ''
// Fetch from CMS, database, etc.
const data = await fetch(`https://api.example.com/${path}`)
return new Response(data.body, {
headers: { 'Content-Type': 'text/html' }
})
}
Monitoring
Cloudflare Analytics
Dashboard → Analytics → Pages
Cloudflare Speed
Check Core Web Vitals at: https://dash.cloudflare.com/{zone}/speed
Error Logs
Dashboard → Cloudflare Pages → Your project → Deployment logs
Limits
- Build timeout: 30 minutes (free), 60 minutes (paid)
- Build memory: 512MB (free), 8GB (paid)
- File size: 100MB per file
- Deployments: Unlimited
Best Practices
- Use a monorepo? Set the root directory in Pages settings
- Environment-specific config: Use different
ssr.config.jsvia environment - Preview deployments: Cloudflare creates preview URLs for PRs
- Branch protection: Configure in GitHub, not Cloudflare