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.
4 · Redirect apex → www (301), preserve the query string
Without this you have two live addresses — domain.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