Cloudflare Pages — static hosting + serverless functions.
Pages hosts static assets on Cloudflare's global CDN with optional Pages Functions — edge Workers that run alongside your static files. It's the standard deployment target for all 2nth.ai frontend sites and client portals.
Free plan: unlimited sites, 500 builds/month, unlimited bandwidth.
# Build your site first, then:
wrangler pages deploy ./dist --project-name=my-site
# With a dirty git state (no CI):
wrangler pages deploy ./dist --project-name=my-site --commit-dirty=true
First deploy to a new project requires creating it first:
wrangler pages project create my-site --production-branch=main
name = "my-site"
pages_build_output_dir = "./dist"
compatibility_date = "2024-12-01"
compatibility_flags = ["nodejs_compat"]
[[d1_databases]]
binding = "DB"
database_name = "my-site-db"
database_id = "your-d1-id"
[[kv_namespaces]]
binding = "KV"
id = "your-kv-id"
Functions live in functions/ at your project root. File path = URL path.
functions/
api/
chat.ts → /api/chat
health.ts → /api/health
_middleware.ts → runs before every request
// functions/api/chat.ts
import type { PagesFunction } from '@cloudflare/workers-types';
interface Env {
DB: D1Database;
KV: KVNamespace;
AI: Ai;
}
export const onRequestPost: PagesFunction<Env> = async (ctx) => {
const body = await ctx.request.json();
// Same as a Worker — full access to bindings
return Response.json({ ok: true });
};
wrangler pages dev ./dist # serve static + functions locally
wrangler pages dev ./dist --port=3000
Bindings (D1, KV, AI) work locally with .dev.vars for secrets:
# .dev.vars
ANTHROPIC_API_KEY=sk-ant-...
Connect your GitHub repo in the Cloudflare dashboard:
main → deploys to your-site.pages.devwrangler pages deploy vs wrangler deploy: Pages uses wrangler pages deploy; Workers use wrangler deploy. They are different commands.functions/ relative to project root, not inside dist/._ prefix for special files: _middleware.ts, _not-found.ts, _headers, _redirects — Cloudflare handles these specially.index.html or Cloudflare will serve a blank page.wrangler pages secret put SECRET_NAME --project-name=my-site.