Lynxz Blog Lynxz Blog
tutorial cloudflare deployment

How to Deploy a Website Free Forever on Cloudflare Pages (Complete Guide)

The exact workflow I use to deploy static sites with unlimited bandwidth for $0 — wrangler commands, gotchas, and the free-tier limits that actually matter.

Lynxz ·

Cloudflare Pages hosts static websites with unlimited bandwidth and unlimited requests on the free plan. No credit card, no trial, no “fair usage” asterisk that silently bills you later.

This is the exact workflow I use for every project I ship, including Lynxz Tools. It takes about five minutes once the CLI is set up.

What “free forever” actually means

Let’s be precise about the limits, because “free tier” claims deserve scrutiny. Here’s what the Cloudflare docs actually promise on the free plan:

ResourceFree tier limit
Static asset requestsUnlimited
Static bandwidthUnlimited
Builds500 per month
Custom domainsUnlimited
Workers (dynamic) requests100,000 per day
Workers KV reads100,000 per day
D1 database rows read5 million per day

For a blog or a client-side tools site, you will almost certainly never touch the Workers quota — every request is served as a static file. The 100k/day Worker limit only applies if you add serverless functions.

If a Worker exceeds the daily limit, Cloudflare returns Error 1027 and stops serving it — so budget accordingly if you ever add dynamic features.

Step 1: Install the CLI

npm install -g wrangler
# or run it on demand without installing:
npx wrangler --version

Authenticate once:

npx wrangler login

This opens a browser and stores the credential locally. On a headless server or CI, use an API token instead:

export CLOUDFLARE_API_TOKEN=your-token-here

Step 2: Create the project (once)

npx wrangler pages project create my-site --production-branch=main

The --production-branch=main flag matters more than it looks. I learned this the hard way: if your local git branch is master but the project expects main, the root alias (my-site.pages.dev) returns 404 and only the branch alias works. Rename your branch before the first deploy:

git branch -m main

Step 3: Deploy

npx wrangler pages deploy . --project-name=my-site --commit-dirty=true

That’s it. Wrangler uploads the files and prints two URLs:

  • A deployment-specific URL like abc123.my-site.pages.dev — every deploy gets one, great for previews
  • The production alias my-site.pages.dev — updates automatically when you deploy from the production branch

The --commit-dirty=true flag deploys uncommitted changes. Skip it if your CI handles commits separately.

Step 4: Add a custom domain (optional)

In the Cloudflare dashboard: Workers & Pages → your project → Custom domains → Set up a domain.

Two things worth knowing:

  1. Buy domains through Cloudflare Registrar if you can — it sells at cost (roughly $10/year for a .com), no markup like most registrars.
  2. Pages issues SSL automatically. You don’t configure certificates.

Security headers (the part most guides skip)

Static hosting doesn’t mean security doesn’t matter. Cloudflare Pages reads a _headers file at your project root. Mine for Lynxz Tools uses a strict Content-Security-Policy:

/*
  Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; font-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'; form-action 'none'; frame-ancestors 'none'
  Strict-Transport-Security: max-age=31536000; includeSubDomains
  X-Content-Type-Options: nosniff
  X-Frame-Options: DENY
  Referrer-Policy: strict-origin-when-cross-origin

That policy has no unsafe-inline — which is only possible because the site has zero inline scripts and zero inline style attributes. It’s a forcing function: the browser itself now verifies the code behaves as claimed.

Note: if you run Google AdSense (like this blog will), you must allow 'unsafe-inline' for script and style sources, because AdSense injects inline code. Security config is always a trade-off against your monetization stack — know which one you’re choosing.

The gotchas I hit so you don’t have to

1. Production branch mismatch. Covered above — rename to main before the first deploy, or your root alias 404s.

2. Edge cache makes verification confusing. After deploying, curl on the root URL can serve a stale HTML copy for a minute or two while other files update instantly. My workaround: verify via the deployment-specific URL, or add a cache-busting query string and compare byte sizes against the local file.

3. The project must exist first. wrangler pages deploy fails if the project doesn’t exist yet. Run pages project create once, then deploy as many times as you want.

4. _headers is served as a file. The file itself is publicly readable at /_headers. It contains no secrets (security headers are visible in every response anyway), but don’t put anything sensitive in it.

5. Subrequests count differently. Workers pricing counts inbound requests; Cloudflare doesn’t bill for subrequests your Worker makes. Good to know before you panic about API calls inside a function.

When to graduate from Pages

Cloudflare Pages is free-tier perfection for static sites. You’d outgrow it if:

  • You need server-side rendering on every request (→ Workers or a framework on Workers)
  • You need a database with complex queries (→ D1, also free-tier generous, or Neon/Upstash)
  • You need always-on processes (→ Workers with Cron Triggers)

For 95% of websites — blogs, portfolios, documentation, client-side tools — the free tier is genuinely all you’ll ever need.

Deploy checklist

  • wrangler login done once
  • pages project create done once with --production-branch=main
  • Local branch renamed to main
  • _headers with security headers in project root
  • robots.txt and sitemap.xml in place
  • Deploy, then verify every route with curl -s -o /dev/null -w "%{http_code}"

The whole setup costs $0 and about ten minutes. The bandwidth is unlimited, the SSL is automatic, and the URLs are production-grade. There’s very little reason to pay for static hosting in 2026.

Written by Lynxz. If this helped you, consider sharing it or supporting the free tools at lynxz-tools.pages.dev.