Workers vs Pages, the two-account split, and the pipeline every new 2nth project follows — written into a site that is itself deployed the recommended way. The strip below is not decoration: it is a live response from this site's own Worker, computed the moment you loaded the page.
↑ fetched from /api/edge — a route served by
src/index.ts, not by a static file. This is the part a plain static host cannot do.
Pages and Workers are two generations of the same idea: put your site on Cloudflare's network. Pages came first as a static-site host with bolt-on functions. Workers is the general compute platform — and since it learned to serve static assets natively, it does everything Pages does, plus everything Pages can't (Durable Objects, cron triggers, queues, real observability).
Cloudflare has effectively frozen Pages. New platform features land on Workers only, and the docs point anyone hosting many sites at Workers.
| You are building… | Deploy as | Because |
|---|---|---|
| A static site — Astro, plain HTML, a framework build | Worker + assets | One assets.directory line in config. Static file requests are free and don't count as Worker invocations. |
| A site that also needs an API, auth, form handler | Worker + assets | Same project: files served first, unmatched routes fall through to your code. This site is the example. |
| A pure API / service / MCP server | Worker | No assets block at all. Add D1/KV/R2/Queues as bindings. |
Something needing a *.2nth.ai hostname before the zone moves |
Pages (exception) | Pages subdomains work cross-account via CNAME from the imbila zone; Workers hostnames can't. Temporary, until the 2nth.ai zone cutover. |
| Anything touching an existing imbila project | Leave it there | Legacy stays in imbila. D1/KV/R2 state cannot move between accounts. Redeploys of existing projects are fine; nothing new is created there. |
Every new project follows one path. Repo in the 2nth-ai GitHub org, config pinned to the 2nth Cloudflare account, built by Workers Builds on every push.
2nth-ai org. wrangler.jsonc pins account_id.npm run dev → wrangler serves assets + Worker at localhost.‹name›.2nth.workers.dev. Custom domain after the zone cutover.Escape hatch: npm run deploy ships from a laptop through the same
config — useful for a first deploy or an emergency, and exactly what the pinned
account_id makes safe.
The whole deployment is one config file and one small Worker. This is the entire
wrangler.jsonc:
{
"name": "deploy-2nth", // = repo name = project name, always
"account_id": "aa0c1ccd…129e39", // pinned: the 2nth account, never imbila
"main": "src/index.ts", // the code half (optional for pure-static)
"compatibility_date": "2026-07-19",
"compatibility_flags": ["nodejs_compat"],
"assets": { "directory": "public" }, // the static half — this replaces Pages
"observability": { "enabled": true } // logs on by default, always
}
And the Worker that answered the hero strip — requests for real files never reach it;
only unmatched routes like /api/edge fall through:
export default { async fetch(request) { const url = new URL(request.url); if (url.pathname === "/api/edge") { return Response.json({ colo: request.cf?.colo, // which datacentre answered city: request.cf?.city, servedAt: new Date().toISOString(), }); } return Response.json({ error: "No such route" }, { status: 404 }); }, } satisfies ExportedHandler<Env>;
aa0c1ccd09eb8e6a1d5003d12c129e39 · Workers Paid
2nth-ai GitHub org*.2nth.workers.dev3db4fadd9630027337f8ae204d570e81 · at the Pages cap
pages_build_output_dir = "." — the Pages dialect.
A Workers build runs wrangler deploy, which doesn't speak it. The fix when
migrating any old repo: swap that line for assets.directory, move Pages
Functions from functions/ into a Worker route, and pin the
account_id. Until then the live site keeps serving from imbila Pages — a
failed build in the new account breaks nothing.
Other traps for the migration file: Workers custom domains need the zone in the same account (wait for the cutover) · zone subscriptions like the old Pro plan don't move with a zone · D1/KV/R2 never crosses accounts — export and re-import, or leave the project where its data lives.