Cloudflare Pages: The Complete Guide for Static Landing Pages

Cloudflare Pages gives indie hackers a fast path from git push to global CDN without managing servers. For a static landing page—HTML, CSS, a few images, and maybe a build step—it is often the best default: free tier is generous, SSL is automatic, and your site sits on the same network that blocks bots before they hit your origin. This guide assumes you are shipping a marketing or waitlist page, not a full-stack app. We cover setup, domains, previews, headers, redirects, and the mistakes that eat Saturdays.

What Cloudflare Pages is (and is not)

Pages is a static hosting product tied to Git. You connect a repository; Cloudflare clones it, runs an optional build command, and publishes the output directory to edge locations worldwide. It is not WordPress hosting, and it is not a database. Dynamic features—forms, search, auth—come from third-party services or Cloudflare Workers paired with Pages Functions. That separation is a feature: your landing page stays fast and cheap while you validate demand.

  • Static assets: HTML, CSS, JS, images, fonts, PDFs
  • SSG output: Eleventy, Hugo, Astro, Next static export, Vite build
  • Preview deployments: every branch or PR gets a unique URL
  • Custom domains: apex and www with automatic HTTPS
  • Not included: long-running Node servers, MySQL, cron on the origin

Prerequisites and account setup

Create a Cloudflare account and a GitHub (or GitLab) repository with your site files. If you use plain HTML, your repo root can be the deploy root. If you use a generator, commit package-lock or pnpm-lock so builds are reproducible. Turn on two-factor authentication on both Cloudflare and Git—your production marketing site should not be one stolen password away from defacement.

  • Cloudflare account with email verified
  • Git repo with README noting build command and output folder
  • Production branch named main (or adjust in settings)
  • Local test: site opens correctly with npx serve or python -m http.server
  • Form endpoint chosen and tested locally with curl or browser

Create your first Pages project

  1. In Cloudflare dashboard: Workers & Pages → Create → Pages → Connect to Git
  2. Authorize GitHub and select the repository
  3. Framework preset: None (or pick Astro/Hugo if applicable)
  4. Build command: leave empty for plain HTML; otherwise npm run build
  5. Build output directory: / for plain HTML; dist or _site or out for common SSGs
  6. Deploy and wait for the first green checkmark

Open the *.pages.dev URL on your phone over LTE. Check hero image, form, and tap targets. The first deploy is your baseline; screenshot Lighthouse scores now so you can compare after adding analytics or fonts.

Build settings for popular static stacks

StackBuild commandOutput directory
Plain HTML(none)/
Vitenpm run builddist
Astronpm run builddist
Hugohugopublic
Eleventynpx @11ty/eleventy_site
Next.js staticnpm run buildout

Custom domains without DNS pain

Add your domain under Pages → Custom domains. If the domain already uses Cloudflare DNS, the CNAME or apex setup is mostly click-through. If DNS lives at Namecheap or Google Domains, either transfer DNS to Cloudflare (recommended for simplicity) or add the records Cloudflare shows you. Apex domains use CNAME flattening on Cloudflare; www should CNAME to your Pages project.

Pick a canonical host: www or apex, not both without redirects. Duplicate URLs split SEO and confuse analytics. Set one as primary in Pages; add a redirect rule so the other permanently forwards. Wait for TLS certificates to reach Active status before posting the link publicly—first visitors remember certificate warnings.

  • Primary domain set in Pages settings
  • Redirect www → apex (or apex → www) with 301
  • HTTPS always; no mixed content warnings in browser console
  • Update canonical link and OG:url tags to match primary host
  • Old pages.dev URL still works; optional redirect to custom domain

Environment variables and build secrets

If your build injects analytics IDs or public API endpoints, store them in Pages → Settings → Environment variables. Production and Preview can differ—use Preview without production keys when testing. Never embed private API secrets in client-side JS; anything in the built HTML is public. For waitlist forms, the form provider URL is fine in HTML; your admin API key is not.

Preview deployments for safe iteration

Every push to a non-production branch generates a preview URL. Use previews to share copy drafts with cofounders or beta users without touching production. Name branches after the experiment: headline-outcome-test, not fix-stuff. When you merge to main, production updates in usually under two minutes. Document preview URLs in PR descriptions so feedback stays traceable.

_redirects and _headers files

Cloudflare Pages honors Netlify-style _redirects and _headers in your publish directory. Put them in the output folder (often project root or dist). Redirects fix moved paths, force HTTPS variants, and send old campaign slugs to the homepage. Headers add security and caching without a Worker.

# _redirects
/old-pricing /#pricing 301
http://www.example.com/* https://example.com/:splat 301

# _headers
/*
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
/assets/*
  Cache-Control: public, max-age=31536000, immutable

Cache immutable hashed assets aggressively; keep HTML shorter cache or no-cache if you ship frequent copy tests. Misconfigured cache on HTML is why you "deployed but still see old headline"—purge CDN cache or bump a query string only as a last resort.

Forms, Functions, and where logic lives

Pure static forms POST to external endpoints—simplest and enough for waitlists. If you need custom validation, honeypot scoring, or Slack notifications, add a Pages Function in functions/api/submit.js that proxies to your backend. Keep functions small; cold starts are usually fine for form volume at indie scale. Log errors to console in preview, wire alerting only after you have paying customers.

Performance on Cloudflare by default

You already get HTTP/3, Brotli, and global anycast. Your job is not to defeat the CDN but to ship lean assets: responsive images, system or subset fonts, minimal third-party scripts. Use Cloudflare Web Analytics or Zaraz if you want lighter tracking. Each analytics script is another DNS lookup and parse cost on mobile.

  1. Compress hero image; prefer WebP or AVIF with PNG fallback if needed
  2. Load fonts with font-display: swap and limit weights to 2
  3. Defer non-critical JS; avoid jQuery for one toggle
  4. Preconnect only to domains you actually request
  5. Run Lighthouse in incognito after deploy, not only locally

Security checklist for public marketing sites

  • 2FA on Cloudflare and Git provider
  • Least-privilege GitHub app access—only repos that need deploy
  • No secrets in repo; rotate if accidentally committed
  • Security headers via _headers file
  • Dependency updates for build tooling monthly
  • Form spam: honeypot + provider-side filtering

Troubleshooting deploy failures

Build fails: read the log line by line. Ninety percent are Node version mismatch, missing lockfile, or wrong output directory. Set NODE_VERSION in environment variables to match local. Site 404 after success: your index.html is not at the output root—fix output path or add index in public folder. Form works locally but not prod: mixed content (HTTP form on HTTPS site) or wrong action URL on production branch only.

  • 404 on all routes: wrong publish directory or missing index.html
  • Assets 404: absolute paths like /assets/... break on preview subpaths—use relative or root-relative consistently
  • Old content after deploy: HTML caching or browser cache—hard refresh or fix Cache-Control
  • SSL pending: DNS not propagated; wait up to 24 hours, usually minutes on Cloudflare DNS
  • Build OOM: reduce image sizes in repo or split large binaries to R2/external CDN

Production launch day sequence

  1. Merge final copy to main; confirm production deploy green
  2. Verify custom domain, www redirect, and SSL active
  3. Submit test form from mobile; confirm notification arrives
  4. Check robots.txt allows indexing; submit sitemap in Google Search Console
  5. Paste OG debugger URL for Slack/X/Twitter card preview
  6. Enable uptime monitor on apex and one deep link
  7. Tag release in git; note deploy time in changelog

Cloudflare Pages is infrastructure you configure once and forget for months. Invest an afternoon in domains, redirects, and headers so every future push is a one-click copy iteration—not another fire drill.

Wrangler CLI for power users

Wrangler lets you deploy without opening the dashboard—useful in CI or when you need a quick upload from a laptop at a café. Install with npm, authenticate once, then run pages deploy against your build output folder. Keep wrangler.toml in the repo with project name and compatibility flags documented. CLI deploys do not replace Git for day-to-day; they are escape hatches when GitHub is down or you need an emergency rollback asset.

npm i -g wrangler
wrangler login
wrangler pages deploy ./dist --project-name=my-landing

Pairing Pages with R2 for large assets

If your repo balloons with PDFs, 4K screenshots, or demo videos, move binaries to R2 and reference public URLs in HTML. Smaller repos mean faster clones and builds. Set Cache-Control on R2 objects to match immutable assets. Keep HTML on Pages so copy deploys stay git-driven while heavy media ships separately.

Access control for client previews

Cloudflare Access can gate preview URLs behind email PIN or SSO when you share work-in-progress with design partners who should not see unfinished pricing. Production stays public. Document which branch triggers Access so you do not accidentally lock your homepage during a misconfigured rule experiment.

Monorepo and multiple projects

If marketing site and docs live in one repo, set build output to the marketing folder only or use a monorepo build script that outputs to dist. Separate Pages projects for docs versus marketing give independent deploy cadences—useful when docs need weekly churn but homepage changes monthly. Document which repo path maps to which project in README so future you does not wire the wrong branch.

Analytics without slowing Pages

Prefer lightweight analytics loaded async at end of body. Cloudflare Web Analytics adds minimal weight. If you need conversion goals, tag form submit success with a small inline script firing one event—avoid loading full GTM on a ten-field waitlist. Review network tab after each new embed; marketing people love tags that cost 400 KB.

Save your final Pages settings in the repo as docs/DEPLOY.md: project name, build command, output dir, env vars, primary domain. Future collaborators should deploy without DMing you for screenshots of the dashboard.

Related: Deploy guides hub How-to articles Static templates Tools directory Free landing kit

Is Cloudflare Pages free enough for a launch?

Yes for typical indie landing traffic. The free tier includes unlimited static requests and generous build minutes. You pay when you add paid Workers features, R2 at scale, or enterprise support—not for a waitlist page with modest traffic.

Pages vs Workers: which do I need?

Pages alone for static HTML and SSG output. Add Workers or Pages Functions when you need edge logic—A/B redirects, form proxies, or lightweight APIs. Start without them.

Can I deploy without Git?

Yes via Wrangler CLI direct upload for emergencies, but Git-connected deploys are the workflow you want for history, previews, and rollbacks.

How do I roll back a bad deploy?

In Pages → Deployments, select a previous successful deployment and Retry deployment or Rollback to that version. Keep copy changes small so rollbacks are painless.

Does Cloudflare replace my email or form tool?

No. Pages hosts files. Email capture still uses Formspree, Mailchimp embed, Buttondown API, or a Worker you write. DNS on Cloudflare can route email with MX records elsewhere.

Deploy on Cloudflare Pages today

Use our deploy guides and free kit to connect your repo, set a custom domain, and ship a waitlist-ready page without server admin.

Open deploy guides Download free kit