Cloudflare / for AI-assisted agencies
Your AI builds it. Cloudflare makes it real.
You've had Claude, Cursor or Lovable build you something good — and then had no idea what to do with the folder it left on your laptop. Cloudflare is where that folder becomes a real web address you can send to a client. It's free, your AI can do the whole thing for you, and it takes about sixty seconds.
What this actually is
Cloudflare is free hosting your AI can drive by itself.
Hosting means: the place your website lives so that other people can open it. Normally that's a monthly bill — Webflow, Wix, Squarespace, a server somewhere. Cloudflare gives it away on a free plan that is genuinely generous, and it has one property none of the others have: it is fully controllable from a command line, which means your AI can do it without you touching a dashboard, a builder, or a settings page.
That's the whole unlock. You describe what you want, your AI builds it, your AI ships it, and you get a link back. No hand-off, no "now export this and upload it somewhere."
If you are running an AI-assisted agency, this is the missing half of your delivery. You already generate the work. This is how the work gets out.
The five words, in plain English
You'll hit these five terms constantly and every guide assumes you know them. You don't need to understand them deeply — you just need to stop being confused when your AI says one.
- A domainthe address
- The address of your shop:
yourbusiness.com. You rent it by the year from a registrar. It is just a name — it holds nothing and does nothing on its own. It points at wherever you tell it to point. Roughly $10–15 a year · Cloudflare sells them at cost - DNSthe signpost
- The internet's address book. It is what connects the name to the actual building. When someone types your domain, DNS is the thing that says "that address means this place, over here." Cloudflare is one of the best address books in the world, and it's free. Free · unlimited domains
- Cloudflare Pagesthe building
- Where your files actually live and get handed to visitors. It serves static sites — meaning the pages are already written, and everyone who visits gets the same thing. Landing pages, reports, documentation, one-pagers, dashboards that just display. This covers about 80% of what you'll ever want to ship. Free · unlimited visitors · unlimited bandwidth
- A Workerthe member of staff
- A static page can't do anything — it can't check a password, save a form, or call an AI. A Worker can. It's a small piece of code that runs the instant someone visits, and it can think, check, save and fetch. In normal language: a Worker is a backend without a server. It's how a page becomes an app. Free up to 100,000 visits a day · then $5/month
- A subdomaina room in the building
- Anything in front of your domain:
docs.yourbusiness.com,reports.yourbusiness.com,acmecorp.yourbusiness.com. You get unlimited of them, free, off the one domain you already rent. This is how you give every client their own address without buying anything. Free · unlimited
One more you'll see your AI type: wrangler. That's the delivery van — the command that moves your files from your laptop into the building. You will never need to learn it. Your AI runs it.
What you actually get
Five things you can do this week that you probably can't do today.
Send clients a link, not a Google Doc.
Every update, report, audit and handover becomes a proper branded page at your own address. Instantly more expensive-looking than a shared Doc, and it takes the same time to make because your AI writes and ships it in one go. The perceived-value jump here is the single fastest win on this list.
Replaces Google Docs · Notion shares · PDF attachments
Give a prospect a free working app before they've paid you a penny.
Build a genuinely useful small tool for one named prospect — their branding, their numbers, their problem — put it on a web address, and send it to them with no ask attached. It costs you near-nothing and it is very hard to ignore. This is the highest-leverage outbound play on the list, and it only works because hosting is free.
Replaces the cold pitch nobody answers
Ship a landing page or lead magnet in an afternoon.
Not a sprint, not a project, not a developer. A page is a copy-and-config job: your AI writes the HTML, you fix the words, it deploys. Drop in an email-capture embed and the email tool handles the rest.
Replaces Webflow · Wix · Framer · Squarespace
Internal dashboards and tools your team actually uses.
Onboarding flows, trackers, calculators, monitors, checklists. The small internal things you currently pay a per-seat subscription for, or do in a spreadsheet because building them was never worth it. Now it's worth it.
Replaces one SaaS subscription per internal tool
Add logins, forms, databases and AI chat — with no server.
This is the offer upgrade. Once you can put a login and a database behind a page, you stop selling "an automation" and start selling an app. Same build effort, different price bracket, and far harder for a client to cancel.
Replaces a VPS · a hosting bill · hiring a developer
What it costs
Verified against Cloudflare's published limits, August 2026. The honest summary: for what an agency does, it is free, and it stays free for a very long time.
| What | Free plan | When you'd pay |
|---|---|---|
| Websites (Pages projects) | 100 per account | Never, realistically — and there's a way around it (Tab 3) |
| Visitors / bandwidth | Unlimited | Never |
| Deploys | 500 builds / month | Never |
| Custom domains | 100 per site | Never |
| Backend requests (Workers) | 100,000 / day | $5/month, which covers 10 million/month |
| HTTPS padlock / SSL | Free, automatic | Never |
| A domain name | Not free | ~$10–15/year, sold at cost |
To put 100,000 requests a day in perspective: that is a page being loaded roughly once a second, all day, every day. If you cross it, the fix is five dollars.
Start — five steps
Do these once. After that, shipping anything is one sentence.
-
Make a free Cloudflare account
Go to
dash.cloudflare.com/sign-up. Email and a password. No card, no trial, no sales call.Two minutes. Don't buy anything and don't add a domain yet — you don't need one to start.
-
Copy two things down
You need an Account ID and an API token. Both live in the same dashboard.
Account ID — click Compute (Workers) in the left sidebar. The Account ID is on the right-hand side of that page. It's a long string of letters and numbers. Copy it.
API token — click your profile icon (top right) → Profile → API Tokens → Create Token. Choose Create Custom Token, then under Permissions pick Account → Cloudflare Pages → Edit. Create it, and copy the token. It is only shown once — paste it somewhere now. -
Put them in a file called
.envIn the folder your project lives in, make a plain text file named.env(yes, starting with a dot) containing exactly two lines:CLOUDFLARE_API_TOKEN=your-token-here CLOUDFLARE_ACCOUNT_ID=your-account-id-here
Or skip this entirely — paste the two values into your AI and say "put these in a .env file for me." It'll do it. Never paste a token into a chat that isn't yours, and never commit
.envto a public repo. - Paste the prompt Open your project in Claude Code, Cursor, Codex or whatever you use, and paste The Prompt below. It tells your AI everything it needs and, importantly, tells it not to lie to you about whether it worked.
-
Say "deploy it"
You get back a live link ending in
.pages.dev. That link works for anyone, anywhere, immediately. Send it to a client.Want it on your own domain instead of
.pages.dev? That's one more sentence — it's covered in How It Works.
The prompt
Copy this into Claude Code, Cursor, or any coding agent, with your project folder open. The last paragraph is the important one — it's the difference between getting a working link and getting a confident lie.
I want to put this project live on Cloudflare so I can send it to a client as a real link. My Cloudflare API token and Account ID are in the .env file in this folder. If there is no .env file, tell me and stop. Please: 1. Look at this folder and tell me in one sentence what kind of site it is and whether it is ready to deploy. 2. Create the Cloudflare Pages project if it does not exist yet. 3. Deploy the whole folder. 4. Give me the live URL. Rules: - Assume I am not technical. If something is missing (a token, an index.html, a build step), tell me exactly what to do in plain English. Do not assume I know what any of it means. - Do NOT tell me it worked until you have actually opened the live URL yourself and confirmed it loads and shows the right content. "It deployed successfully" is not the same as "it works" - check it. - If anything fails, show me the actual error and what you are going to try next. Do not silently retry or guess.
Works with Claude Code · Cursor · Codex · any agent with terminal access
Then reuse it forever
Once a project is deployed the first time, every future update is just: "push the latest
version live". Your AI already knows the project name and has the credentials. That's the
entire ongoing workflow.
What to do first
Don't try to do all five use cases. Pick the one that's already sitting on your desk:
If you have a client update due this week
Start with use case 01. Take the update you were going to send as a message, and have your AI build it as a page instead. Ship it. It's the lowest-risk first deploy and the reaction tells you everything.
If you're trying to book more calls
Start with use case 02. Pick one prospect you actually want. Build them one small useful thing. Send it with no ask. Do that ten times before you judge it.
If you're paying for Webflow right now
Start with use case 03. Rebuild your current landing page, deploy it, compare. Then cancel the subscription or don't — at least the decision becomes real.
The rest is just detail.
Tab 2 explains what actually happens when you hit deploy, and the three things that will trip you up. Tab 3 is for whoever on your team writes code.
Cloudflare / how it works
What actually happens when you hit deploy.
You don't need this to ship your first page. You need it the first time something doesn't work — so that instead of being stuck, you know which of the four moving parts to point your AI at.
The whole journey
You
A folder with an index.html in it
Upload
Files sent to Cloudflare
Network
Cached in 300+ cities worldwide
Live
yourproject.pages.dev
The part worth understanding: your files get copied to hundreds of cities at once. A visitor in Sydney is served from Sydney, not from wherever you uploaded. That's why it's fast everywhere without you configuring anything, and it's why the free bandwidth isn't a trick — serving cached files is genuinely cheap for them.
The two kinds of thing you can build
Almost every question you'll have comes down to which of these two you're making.
| A static site (Pages) | An app (Pages + a Worker) | |
|---|---|---|
| What it is | Pages that are already written. Everyone sees the same thing. | Pages plus code that runs per visitor and can decide things. |
| Good for | Landing pages, reports, updates, documentation, portfolios, calculators that run in the browser | Logins, saved data, forms that store answers, AI chat, anything per-user |
| Setup | Drop files in a folder. Deploy. | Add a functions/ folder. Deploy. Same command. |
| Cost | Free, unlimited | Free to 100k requests/day, then $5/mo |
| Start here? | Yes — 80% of what you need | Only when a static page genuinely can't do the job |
The good news for anyone non-technical: you don't choose up front and you don't migrate later. A static site becomes an app by adding a folder. It's the same project, same domain, same deploy command. So start static, always.
Putting it on your own domain
A fresh deploy gives you something.pages.dev. That's a real, working,
shareable link — but for client-facing work you'll want your own name on it. Here's the part
that trips up literally everyone, including experienced developers:
Connecting a domain is two separate jobs, not one
Job one: tell your site "you should answer to this name."
Job two: tell the address book "this name points to that site."
Doing only job one is the classic failure. The dashboard will happily show your domain attached and sitting in Pending — forever — because nothing ever told the address book where to send people. It doesn't error. It just never works.
In practice, the sequence is:
- Get the domain onto Cloudflare
Either buy it from Cloudflare directly (simplest — it arrives already connected), or if you
already own it elsewhere, add it to Cloudflare and change its nameservers at your current registrar.
Nameservers = telling the world "Cloudflare is now my address book." It's two fields at GoDaddy/Namecheap/wherever. Takes minutes to swap, up to a few hours to spread everywhere.
- Attach the domain to your site In the Pages project → Custom domains → add it. Or let your AI do it.
- Make sure the DNS record actually exists
This is the step that gets skipped. There must be a record pointing your domain at
yourproject.pages.dev. If you bought the domain from Cloudflare and attached it in the dashboard, this is usually created for you. If you're scripting it, you have to create it yourself — and it uses different credentials to the deploy step, which is why scripts silently half-work. - Wait for the padlock Cloudflare issues the HTTPS certificate automatically once DNS resolves. Usually a few minutes, occasionally 15. A certificate warning in the first ten minutes is normal, not broken.
The trick worth stealing: use subdomains, not new domains
You do not need to buy a domain per client. Buy one domain, then give every client a room
in it: acme.yourbusiness.com, globex.yourbusiness.com. Unlimited, free,
and the HTTPS padlock is covered automatically for one level of subdomain.
One $12/year domain can carry every client page you will ever ship.
The four things that will bite you
These are the ones worth knowing in advance, because each one fails quietly — you won't get an error, you'll get something that looks fine and isn't.
1. A deploy replaces everything
Cloudflare doesn't merge your upload with what's already there. Whatever you deploy becomes the entire site. Deploy a subfolder by accident and every other page in that project disappears.
The fixAlways deploy the whole folder, and keep one project per thing you're shipping unless you have a deliberate reason not to.
2. Your AI will tell you it worked
"Deployed successfully" means the upload finished. It does not mean the page loads, the domain resolves, or the content is right. Agents report the command's exit code and move on.
The fixIt's baked into the prompt on Tab 1 — make it open the live URL and confirm the content before it's allowed to say done.
3. A domain can look like it's working when it isn't
If you have a catch-all rule on your domain (common if it's parked, or came with a website builder), an unconnected subdomain will still resolve — and quietly serve a parking page or the wrong site. It returns a perfectly healthy response.
The fixVerify on content, never on "it loaded." Open it and look at what's actually on the page.
4. Files and code don't go live at the same instant
Only relevant once you've added a backend: your static files and your Worker code update on slightly independent timelines. For a few seconds after a deploy you can get new page + old logic.
The fixIf a check fails within a minute of deploying, wait and check again before you start debugging something that isn't broken.
When you outgrow it
One real ceiling exists and it's worth knowing about before you hit it, because the obvious workaround makes it worse.
You can have 100 Pages projects per account, and Cloudflare says that limit is not routinely increased.
Each separate site is one project. If your model is "every client gets their own site,"
you have a hundred clients' worth of runway and then a wall. The instinctive fix — "we'll stop
buying domains and just use the free .pages.dev ones" — is backwards, because a
.pages.dev address is a project. It consumes the scarce thing faster while saving
the abundant one. Domains have no limit at all.
The actual fix is to stop creating a project per client: one domain, one Worker, and a wildcard so that every client gets a subdomain served by the same deployment. Unlimited clients, one project. That's a build, not a setting — the specifics are in Tab 3.
The general lesson, which is worth more than the Cloudflare detail: count what runs out before you optimise what costs. A monthly charge is visible and feels like the problem. A platform quota is invisible until it stops you dead.
Hand tab three to whoever writes your code.
Config, commands, limits, and the multi-client architecture — plus a manifest any AI agent can read directly.
Cloudflare / technical specifications
Config, commands and constraints.
Everything below is verified against Cloudflare's published limits as of August 2026, or drawn from production deployments. Agent-readable versions: llms.txt · manifest.json.
Stack anatomy
The shape that works, and the division of labour that keeps it cheap: Cloudflare is the edge and the front door; anything long-running or heavy belongs somewhere else.
| Layer | What runs there | Why |
|---|---|---|
| Pages (static) | Built HTML/CSS/JS, assets | Free and unlimited; requests to static assets never touch your quota |
| Pages Functions | functions/**.ts → auto-routed to /api/* | Auth, sessions, form handling, proxying. No server, no cold-start config |
| Workers KV | Config, tenant registries, feature flags | Fast global reads; eventually consistent — not for anything transactional |
| External DB | Postgres over HTTP (e.g. Neon serverless) | Real relational data. Needs nodejs_compat to bundle cleanly |
| External compute | Long jobs, LLM calls, batch processing | Workers cap CPU per request — proxy out, don't compute in |
The rule that shapes everything
Free-plan Workers get 10ms CPU per request. That is ample for routing, auth, validation and proxying — and nowhere near enough for an LLM call or a batch job. So the edge authenticates and forwards; the heavy work happens on compute you control. Waiting on a subrequest doesn't count against CPU time, so proxying is cheap.
The commands
# one-time: create the project npx wrangler pages project create <name> --production-branch main # deploy (idempotent; --branch main pins the stable *.pages.dev alias) npx wrangler pages deploy <dir> --project-name=<name> --branch=main # local dev, with Functions running npx wrangler pages dev <dir> --compatibility-flags=nodejs_compat # secrets (prod). local dev reads .dev.vars instead npx wrangler pages secret put <NAME> --project-name=<name> # standalone Worker (not Pages) — for the wildcard/multi-tenant model npx wrangler deploy -c wrangler.toml # sanity npx wrangler whoami npx wrangler pages project list
Auth via CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID in env
Omit --branch=main and wrangler infers the
branch from git — which produces a preview deployment on a hashed URL rather
than updating the stable alias. This is the most common "I deployed but the site didn't change."
Pages Functions
File-based routing, no router config. Every file exports
onRequest/onRequestGet/onRequestPost and receives a context
object carrying request, env (bindings + secrets), data,
params and next.
functions/ _middleware.ts // runs before everything below it api/ _middleware.ts // scoped to /api/* only health.ts // -> GET /api/health chat.ts // -> POST /api/chat auth/ verify.ts // -> /api/auth/verify conversations/ [id].ts // -> /api/conversations/:id _lib/ // underscore = not routed. shared code lives here env.ts db.ts session.ts
Directories and files prefixed with _ are never
routed — that's where shared helpers go. A _middleware.ts applies to its own
directory and everything beneath it, which makes it the natural place for auth: resolve the session
once, hang the user on context.data, and let downstream handlers assume it.
Guard on identity before anything else
If one deployment serves multiple clients, resolve who the request is for before you touch a token or a database, and make an unknown host a hard 404. Falling through to "whatever was configured" is not a 404 — it's a cross-client data leak.
Configuration
# wrangler.toml — Pages name = "my-project" compatibility_date = "2024-11-01" compatibility_flags = ["nodejs_compat"] # needed for most DB drivers pages_build_output_dir = "dist"
# wrangler.toml — standalone Worker serving many clients off one wildcard name = "my-app" main = "worker/index.ts" compatibility_date = "2024-11-01" compatibility_flags = ["nodejs_compat"] [assets] directory = "dist" binding = "ASSETS" not_found_handling = "single-page-application" # SPA deep links survive refresh run_worker_first = true # else static files answer before your code [[kv_namespaces]] binding = "TENANTS" # hostname -> { config, db_url, secret } id = "<namespace-id>" [[routes]] pattern = "*.yourdomain.com/*" # wildcards work on Workers. NOT on Pages. zone_name = "yourdomain.com"
run_worker_first = true is subtle and important:
without it the static asset server answers first, so a per-client /config.json would be
served from whichever build shipped last — to every client.
The multi-client architecture
The escape hatch from the 100-project ceiling. Instead of a project, a domain, a secret
set and a certificate wait per client, one Worker answers a wildcard host and reads the
Host header to decide who it's serving.
Request
acme.yourdomain.com
Worker
Look host up in KV
Unknown
Hard 404 — never fall through
Hit
Tenant record resolved
Per tenant
config + db url + session secret
Serve
Same handlers, right tenant
Two constraints make this work rather than merely compile:
Wildcards are a Worker feature, not a Pages feature
Pages rejects a wildcard custom domain outright (8000015 Domain is invalid). Worker
routes accept them, and Universal SSL already covers one level of subdomain — so
there is no per-client certificate to wait for either.
Multi-tenant is not "share everything"
Share what is genuinely fleet-wide; keep what is genuinely per-tenant — and that second list is longer than it looks. Anything used as an HMAC key for credentials already issued (magic links, sessions) must stay per-tenant. Unifying it silently invalidates every live login link in the field, with no error anywhere.
Done properly, provisioning a new client stops being a deploy pipeline and becomes a single KV write.
Limits — free plan
Verified against Cloudflare's published documentation, August 2026.
| Resource | Free | Note |
|---|---|---|
| Pages projects / account | 100 | "Not routinely increased." The real ceiling. |
| Builds | 500 / month | 1 concurrent |
| Files per deployment | 20,000 | Max 25 MiB per file |
| Custom domains / project | 100 | No wildcards on Pages |
| Static asset requests | Unlimited | Free on both plans; never hits quota |
| Worker / Functions requests | 100,000 / day | Shared pool. Resets midnight UTC. Over = error 1027 |
| Worker CPU time | 10 ms | Per request. Waiting on I/O doesn't count |
| Subrequests | 50 / request | Plus 1,000 to internal CF services |
| Workers / account | 100 | |
| KV reads / writes | 100k / 1k per day | 1 GB stored. Read-heavy by design |
| Zones (domains) | Unlimited | The abundant resource |
Workers Paid is $5/month minimum, including 10 million requests and 30 million CPU-milliseconds; then $0.30 per additional million requests and $0.02 per additional million CPU-ms.
Wiring a domain by API
The two-call sequence, and the credential split that makes scripted setups fail silently.
# 1. attach the domain to the Pages project — uses the API TOKEN POST /client/v4/accounts/<account>/pages/projects/<project>/domains Authorization: Bearer $CLOUDFLARE_API_TOKEN { "name": "app.yourdomain.com" } # 2. create the DNS record — the Pages token 401s here. Needs the GLOBAL key. POST /client/v4/zones/<zone>/dns_records X-Auth-Key: $CLOUDFLARE_GLOBAL_API_KEY X-Auth-Email: $CLOUDFLARE_EMAIL { "type": "CNAME", "name": "app.yourdomain.com", "content": "<project>.pages.dev", "proxied": true }
Step 1 does not do step 2
Attaching the custom domain creates no DNS record, and the Pages-scoped token cannot write zone DNS. Skip step 2 and the domain sits in Pending indefinitely with no error raised anywhere. Any script that does this should hard-fail, not warn — a warning gets scrolled past and ships an NXDOMAIN to a client.
Verify afterwards over DNS-over-HTTPS (cloudflare-dns.com/dns-query) rather than the
local resolver, which caches the pre-record NXDOMAIN and keeps reporting failure long after the
record has landed.
For agents
This page is machine-readable. Point any coding agent at either file and it has everything it needs to provision, deploy and verify without a human in the loop.
# markdown, for LLM context curl https://<this-host>/llms.txt # structured, for programmatic use curl https://<this-host>/manifest.json
Both are linked from this page's <head> via rel="alternate"
Ship the first one today.
Pick the smallest real thing you owe someone this week and put it on a link. The architecture questions answer themselves once something is live.