Cloudflare Pages
Cloudflare Pages is a static hosting platform with serverless edge functions built in. It serves files from Cloudflare’s global CDN (180+ cities) with zero configuration.
How it works
Section titled “How it works”- Push code to GitHub
- Cloudflare runs
npm run build-> outputs todist/ - Files in
dist/are deployed to every Cloudflare PoP globally - Files in
functions/are deployed as edge functions alongside
The static files and the functions share the same domain - no CORS, no separate API URL.
File structure that matters
Section titled “File structure that matters”public/ _headers <-- security headers applied to every response _redirects <-- URL redirects processed at the edge robots.txt <-- copied to dist/ as-is favicon.svg <-- copied to dist/ as-is
functions/ api/ payment/ create-order.ts <-- becomes POST /api/payment/create-order verify.ts <-- becomes GET /api/payment/verify
wrangler.toml <-- local dev config (not used by Cloudflare Pages in production)Pages Functions
Section titled “Pages Functions”Any .ts or .js file under functions/ becomes a serverless function. The file path maps to the URL path:
functions/api/payment/create-order.ts -> /api/payment/create-orderExport named functions to handle HTTP methods:
type Env = { MY_SECRET: string;};
type PagesContext = { request: Request; env: Env;};
export const onRequestPost = async ({ request, env }: PagesContext) => { const secret = env.MY_SECRET; // from Cloudflare dashboard // ... return new Response(JSON.stringify({ ok: true }), { headers: { "Content-Type": "application/json" }, });};
export const onRequestGet = async ({ request, env }: PagesContext) => { // handle GET};Critical: Use context.env.VAR for secrets. import.meta.env.VAR is undefined at runtime in Cloudflare Workers/Functions.
_headers
Section titled “_headers”Applies HTTP headers to every response. No server config needed:
/* X-Frame-Options: SAMEORIGIN X-Content-Type-Options: nosniff Referrer-Policy: strict-origin-when-cross-origin Strict-Transport-Security: max-age=31536000; includeSubDomains; preload Permissions-Policy: camera=(), microphone=(), geolocation=() Content-Security-Policy: default-src 'self'; ...See the Security headers page for the full CSP template.
_redirects
Section titled “_redirects”https://www.example.com/* https://example.com/:splat 301This redirects all www.* requests to the apex domain. The rule only fires if www.example.com is also added as a custom domain in the Pages project - otherwise Cloudflare never sees those requests.
wrangler.toml
Section titled “wrangler.toml”Used for local development with wrangler pages dev. Not used by Cloudflare Pages in production.
name = "project-name"compatibility_date = "2025-06-23"pages_build_output_dir = "dist"Build settings in the dashboard
Section titled “Build settings in the dashboard”| Setting | Value |
|---|---|
| Build command | npm run build |
| Build output directory | dist |
| Node version | 18+ (set via NODE_VERSION env var if needed) |
Free tier limits
Section titled “Free tier limits”For static sites with Pages Functions:
- Builds: 500/month free
- Bandwidth: Unlimited (static files served from CDN)
- Functions: 100,000 invocations/day free
- Custom domains: Unlimited
More than enough for any client site.