Cloudflare
A 1-part workshop, read in order or jump around.
Automating Cloudflare Pages with the API
The Cloudflare Pages dashboard is fine but I'd rather just curl things. Here's how I set up staging and production branches pointing to different domains without touching the UI.
I needed to set up staging and production domains for my blog on Cloudflare Pages. You can do this through the dashboard but I don't know, I just didn't feel like clicking around. The API is right there.
What I wanted
Pretty straightforward: eduuh.com should serve the main branch of blog-2026, and staging.eduuh.com should serve the staging branch.
Getting API access
Grabbed an API token from the Cloudflare dashboard. You need Account > Cloudflare Pages > Edit, Account > Account Settings > Read, and (if you want to script DNS too) Zone > DNS > Edit.
Keep the token out of git. I stuck mine in .env and added it to .gitignore.
Adding the domains
Attaching custom domains to a Pages project:
# Root domain
curl -X POST \
"https://api.cloudflare.com/client/v4/accounts/{account_id}/pages/projects/blog-2026/domains" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
--data '{"name":"eduuh.com"}'
# Staging subdomain
curl -X POST \
"https://api.cloudflare.com/client/v4/accounts/{account_id}/pages/projects/blog-2026/domains" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
--data '{"name":"staging.eduuh.com"}'Making staging actually point to staging
Here's where I got stuck. Both domains were now attached, but both were serving main. Not what you want from a staging URL.
You have to explicitly tell Cloudflare which branch a domain should serve:
curl -X PATCH \
"https://api.cloudflare.com/client/v4/accounts/{account_id}/pages/projects/blog-2026/domains/staging.eduuh.com" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
--data '{"branch":"staging"}'This was the key bit I couldn't figure out from the dashboard. The API made it obvious.
DNS while I'm at it
My domain's already on Cloudflare, so I figured I'd set up the DNS records via API too:
# Root domain
curl -X POST \
"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
--data '{
"type": "CNAME",
"name": "@",
"content": "blog-2026-290.pages.dev",
"proxied": true
}'
# Staging
curl -X POST \
"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
--data '{
"type": "CNAME",
"name": "staging",
"content": "blog-2026-290.pages.dev",
"proxied": true
}'Both point to the same pages.dev subdomain - Cloudflare routes to the right branch based on the domain config we set earlier.
That's it
| Domain | Branch |
|---|---|
eduuh.com | main |
staging.eduuh.com | staging |
Push to main, production updates. Push to staging, staging updates.
The PATCH to map a specific branch to a domain was the thing I couldn't figure out how to do in the dashboard. Might be possible, I didn't look that hard. But the API made it obvious.
Claude figured out the APIs for me. Saved me from digging through docs and clicking around the dashboard.