Notes from production
CloudflareWranglerDeploy

Cloudflare Pages + Wrangler: deploy & point the domain

You can deploy a static site to Cloudflare Pages straight from your terminal with Wrangler (Cloudflare's CLI) — no waiting on CI. Here's the whole path: a safe token, creating and deploying the project, pointing the domain, then making sure www is the one canonical home so *.pages.dev doesn't duplicate your site in the eyes of search engines.

This is the CLI/manual path. For the automated version (one push to main), see CI/CD: GitHub Actions → Cloudflare Pages — it runs the same Wrangler command, just from a runner.

1 · A least-privilege API token

Wrangler needs two things to talk to your account: an Account ID and an API token. Don't use a global token — make one that can only deploy Pages, only on this account. If it leaks, the damage is limited to Pages on one account, not your whole Cloudflare account.

In Cloudflare → My Profile → API Tokens → Create Token (Custom):

Permissions:        Account · Cloudflare Pages · Edit
Account Resources:   Include · <the account that owns the domain>   ← this one only

Then hand it to Wrangler via environment variables:

export CLOUDFLARE_ACCOUNT_ID=<account-id>     # from the dashboard, right sidebar
export CLOUDFLARE_API_TOKEN=<pages-edit-token>

2 · Create & deploy with Wrangler

Create the project once, then deploy the built output (dist/):

npm i -D wrangler

# create the project once — production branch = main
wrangler pages project create my-site --production-branch main

# deploy the static output
wrangler pages deploy ./dist --project-name my-site
# → deployed to https://my-site.pages.dev

Each deploy uploads the folder and returns a *.pages.dev URL. (In this repo, the real deploy command is wrangler pages deploy apps/labs/dist --project-name=pangaea-id.) Your site is now live at my-site.pages.dev — but that isn't your domain yet.

3 · Point the domain: www + apex CNAME → pages.dev

In Cloudflare DNS (your domain's zone), point both hosts at the Pages project. www is the canonical address; the apex follows via Cloudflare's CNAME flattening (a CNAME at the apex is illegal in plain DNS, but Cloudflare flattens it):

Type   Name   Content              Proxy
CNAME  www    my-site.pages.dev    Proxied   ← orange cloud
CNAME  @      my-site.pages.dev    Proxied   ← apex, flattened by Cloudflare

Both must be Proxied (orange cloud) — the Redirect Rule in the next step only runs when traffic passes through Cloudflare. Add www.[domain] as a custom domain on the Pages project so its TLS certificate is issued.

1 · wrangler pages deploy ./dist→ my-site.pages.dev2 · DNS: www + apex (CNAME)→ my-site.pages.dev · Proxied3 · Redirect Rule: apex → www301 · query string preserved4 · canonical → wwwapex + pages.dev consolidate to one indexed homeone canonical address — not three twin sites
The build goes up to pages.dev; DNS points www + apex there; a Redirect Rule 301s the apex to www (query preserved); and canonical keeps www the only address that's indexed.

4 · Redirect apex → www (301), preserve the query string

Without this you have two live addressesdomain.com and www.domain.com — serving the same content. That's a duplicate. Create a Single Redirect Rule in Cloudflare (Rules → Redirect Rules):

When incoming requests match:  hostname  eq  "domain.com"        (apex)
Then:  Dynamic redirect → concat("https://www.domain.com", http.request.uri.path)
       Status 301 (permanent) · Preserve query string: ON

301 (not 302) so search engines move their authority to www. Preserve query string is mandatory: it keeps ?utm_source=… intact through the redirect, so your lead attribution isn't lost at the door (see how the /book form handles submissions — its attribution depends on the query string surviving). The full apex→www detail lives in Root → www: the 301 redirect.

Make www the one canonical home (the .pages.dev trap)

This is the step people miss. The my-site.pages.dev URL stays public and crawlable — so Google can find your site at three addresses: www.domain.com, the apex, and *.pages.dev. Same content, three URLs = SEO duplication, and authority gets split.

The redirect above handles the apex. For pages.dev, the defense is a consistent canonical:

<!-- on every page, whatever host served it -->
<link rel="canonical" href="https://www.domain.com/this-page" />

This site does it from a single constant — SITE_ORIGIN = 'https://www.pangaea.id' — that builds every canonical. So even if someone opens the pages.dev or apex version, the canonical tag still points at www, and Google consolidates everything there. To be stricter, you can disable the pages.dev alias or add a noindex for that host.

Common questions

Is the .pages.dev URL a problem for SEO?

It can be, if left alone. my-site.pages.dev is public and crawlable, so it duplicates your site on another host. The defense is a consistent canonical: every page points rel=canonical at your www domain, whatever host served it, so search engines consolidate the duplicates. To be stricter, disable the pages.dev alias or add a noindex for that host.

What permissions does the Wrangler token need?

Only Account · Cloudflare Pages · Edit, with Account Resources limited to the account that owns the domain. That's enough to create a project and deploy, and no more. Avoid a global token; if this scoped one leaks, it can only touch Pages on one account.

Can the apex be a CNAME? Isn't that illegal in DNS?

Right — a CNAME at the apex is illegal in standard DNS, but Cloudflare supports CNAME flattening: you enter a CNAME at the apex (@) and Cloudflare serves it as a flattened A/AAAA record. So the apex can point at my-site.pages.dev. Since here the apex is immediately 301'd to www, it just needs to be proxied so the Redirect Rule runs.

Where this fits

This is the CLI deploy path behind CI/CD: GitHub Actions → Cloudflare Pages (the same Wrangler command, run automatically), leaning on the DNS from Point the domain at Cloudflare and the canonical redirect from Root → www.

Sources

  1. Cloudflare Pages — documentation
  2. Wrangler — pages commands
  3. Cloudflare — Single Redirects
  4. Cloudflare — CNAME flattening
  5. Google — canonical & duplicate URLs