xiaohongshu
xiaohongshu
cloudflare-pages
Cloudflare Pages - Full-stack JAMstack platform with global CDN, serverless functions, Git integration, and native bindings to Workers, KV, R2, D1, and Durable Objects
Full skill instructions
Cloudflare Pages is a JAMstack platform for deploying full-stack applications to Cloudflare's global network. It combines static site hosting with serverless Pages Functions, automatic Git deployments, and native integrations with the Cloudflare ecosystem (Workers, KV, R2, D1, Durable Objects).
Key Value Proposition: Deploy static sites and full-stack applications with zero configuration, automatic preview deployments, unlimited bandwidth, and seamless access to Cloudflare's edge computing and storage services.
┌─────────────────────────────────────────────────────────────────┐
│ Cloudflare Pages │
│ Global Edge Network (300+ PoPs) │
└─────────────────────────────────────────────────────────────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Static Assets │ │ Pages Functions│ │ Bindings │
├───────────────┤ ├───────────────┤ ├───────────────┤
│ • HTML/CSS/JS │ │ • /functions/ │ │ • KV │
│ • Images │ │ • Middleware │ │ • R2 │
│ • _headers │ │ • Routing │ │ • D1 │
│ • _redirects │ │ • TypeScript │ │ • DO │
│ • _routes.json│ │ • Workers API │ │ • Queues │
└───────────────┘ └───────────────┘ │ • AI │
│ │ └───────────────┘
└─────────────────────┼─────────────────────┘
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Deployment │ │ Domains │ │ Builds │
├───────────────┤ ├───────────────┤ ├───────────────┤
│ • Git (GH/GL) │ │ • Custom │ │ • 20 min max │
│ • Direct │ │ • *.pages.dev │ │ • 500/mo free │
│ • Wrangler │ │ • Preview URLs│ │ • Env vars │
│ • API │ │ │ │ • Presets │
└───────────────┘ └───────────────┘ └───────────────┘
| Resource | Free | Pro | Business | Enterprise |
|---|---|---|---|---|
| Builds/month | 500 | 5,000 | 20,000 | Unlimited |
| Custom domains | 100 | 250 | 500 | 500 |
| Files per site | 20,000 | 20,000 | 20,000 | 20,000 |
| File size | 25 MB | 25 MB | 25 MB | 25 MB |
| Preview deployments | Unlimited | Unlimited | Unlimited | Unlimited |
| Build timeout | 20 min | 20 min | 20 min | 20 min |
| Config | Limit |
|---|---|
| Header rules | 100 max |
| Static redirects | 2,000 max |
| Dynamic redirects | 100 max |
| Redirect line length | 1,000 chars |
| Header line length | 2,000 chars |
| Projects per account | 100 (soft) |
# 1. Connect repository in Cloudflare Dashboard
# Workers & Pages > Create application > Connect to Git
# 2. Configure build settings
# - Production branch: main
# - Build command: npm run build
# - Build output: dist
# 3. Push to deploy
git push origin main
# Install Wrangler
npm install -g wrangler
# Login to Cloudflare
wrangler login
# Create a new project
npx wrangler pages project create my-site
# Deploy to production
npx wrangler pages deploy ./dist
# Deploy preview
npx wrangler pages deploy ./dist --branch=staging
Cloudflare provides presets for 31+ frameworks:
| Framework | Build Command | Output Directory |
|---|---|---|
| React (Vite) | npm run build | dist |
| Next.js | npx @cloudflare/next-on-pages@1 | .vercel/output/static |
| Astro | npm run build | dist |
| SvelteKit | npm run build | .svelte-kit/cloudflare |
| Nuxt | npm run build | dist |
| Remix | npm run build | public |
| Vue (Vite) | npm run build | dist |
| Angular | npm run build | dist/<project> |
| Gatsby | gatsby build | public |
| Hugo | hugo | public |
| Docusaurus | npm run build | build |
| Eleventy | npx @11ty/eleventy | _site |
These are automatically injected during builds:
| Variable | Description | Example |
|---|---|---|
CF_PAGES | Always 1 on Pages | 1 |
CF_PAGES_COMMIT_SHA | Git commit hash | a1b2c3d... |
CF_PAGES_BRANCH | Branch name | main |
CF_PAGES_URL | Deployment URL | https://abc.pages.dev |
CI | Always true | true |
# Set via dashboard: Settings > Environment variables
# Or in wrangler.toml
[vars]
API_URL = "https://api.example.com"
# Secrets (encrypted)
# Set via: wrangler pages secret put SECRET_NAME
my-monorepo/
├── apps/
│ └── web/ # Root directory: apps/web
│ ├── src/
│ └── package.json
└── packages/
Set Root directory to apps/web in build settings.
my-project/
├── functions/
│ ├── _middleware.js # Global middleware
│ ├── api/
│ │ ├── _middleware.js # API-specific middleware
│ │ ├── index.js # GET /api
│ │ ├── users/
│ │ │ ├── index.js # GET /api/users
│ │ │ └── [id].js # GET /api/users/:id
│ │ └── [[catchall]].js # Catch-all
│ └── health.js # GET /health
├── public/
│ └── index.html
└── package.json
// functions/api/hello.ts
export const onRequestGet: PagesFunction = async (context) => {
return new Response(JSON.stringify({ message: "Hello!" }), {
headers: { "Content-Type": "application/json" }
});
};
// Handle multiple methods
export const onRequest: PagesFunction = async (context) => {
const { request } = context;
if (request.method === "POST") {
const body = await request.json();
return new Response(JSON.stringify(body));
}
return new Response("Method not allowed", { status: 405 });
};
// functions/users/[id].ts
export const onRequestGet: PagesFunction = async (context) => {
const { id } = context.params; // string
return new Response(`User: ${id}`);
};
// functions/files/[[path]].ts (catch-all)
export const onRequestGet: PagesFunction = async (context) => {
const { path } = context.params; // string[]
return new Response(`Path: ${path.join("/")}`);
};
// functions/_middleware.ts
export const onRequest: PagesFunction = async (context) => {
// Before handler
console.log(`${context.request.method} ${context.request.url}`);
try {
// Call next handler
const response = await context.next();
// After handler - modify response
response.headers.set("X-Custom-Header", "value");
return response;
} catch (err) {
return new Response("Server Error", { status: 500 });
}
};
// Chain multiple middlewares
export const onRequest = [errorHandler, auth, logging];
async function errorHandler(context) {
try {
return await context.next();
} catch (err) {
return new Response(err.message, { status: 500 });
}
}
async function auth(context) {
const token = context.request.headers.get("Authorization");
if (!token) {
return new Response("Unauthorized", { status: 401 });
}
return context.next();
}
// functions/api/data.ts
interface Env {
KV: KVNamespace;
DB: D1Database;
BUCKET: R2Bucket;
API_KEY: string;
}
export const onRequestGet: PagesFunction<Env> = async (context) => {
const { env, request, params, waitUntil, passThroughOnException } = context;
// Access bindings
const value = await env.KV.get("key");
const result = await env.DB.prepare("SELECT * FROM users").all();
return Response.json({ value, users: result.results });
};
// functions/api/kv.ts
interface Env {
MY_KV: KVNamespace;
}
export const onRequest: PagesFunction<Env> = async ({ env }) => {
// Write
await env.MY_KV.put("key", "value");
await env.MY_KV.put("json", JSON.stringify({ foo: "bar" }));
await env.MY_KV.put("expiring", "data", { expirationTtl: 3600 });
// Read
const value = await env.MY_KV.get("key");
const json = await env.MY_KV.get("json", { type: "json" });
// List
const list = await env.MY_KV.list({ prefix: "user:" });
// Delete
await env.MY_KV.delete("key");
return Response.json({ value, json });
};
# Local development
npx wrangler pages dev ./dist --kv=MY_KV
// functions/api/files.ts
interface Env {
BUCKET: R2Bucket;
}
export const onRequest: PagesFunction<Env> = async ({ env, request }) => {
const url = new URL(request.url);
const key = url.pathname.slice(1);
switch (request.method) {
case "PUT":
await env.BUCKET.put(key, request.body, {
httpMetadata: { contentType: request.headers.get("content-type") }
});
return new Response("Uploaded");
case "GET":
const object = await env.BUCKET.get(key);
if (!object) return new Response("Not found", { status: 404 });
return new Response(object.body, {
headers: { "Content-Type": object.httpMetadata?.contentType || "application/octet-stream" }
});
case "DELETE":
await env.BUCKET.delete(key);
return new Response("Deleted");
}
};
# Local development
npx wrangler pages dev ./dist --r2=BUCKET
// functions/api/users.ts
interface Env {
DB: D1Database;
}
export const onRequestGet: PagesFunction<Env> = async ({ env }) => {
const { results } = await env.DB
.prepare("SELECT * FROM users WHERE active = ?")
.bind(1)
.all();
return Response.json(results);
};
export const onRequestPost: PagesFunction<Env> = async ({ env, request }) => {
const { name, email } = await request.json();
const result = await env.DB
.prepare("INSERT INTO users (name, email) VALUES (?, ?)")
.bind(name, email)
.run();
return Response.json({ id: result.meta.last_row_id });
};
# Local development
npx wrangler pages dev ./dist --d1=DB=<database-id>
// functions/api/counter.ts
interface Env {
COUNTER: DurableObjectNamespace;
}
export const onRequest: PagesFunction<Env> = async ({ env, request }) => {
const id = env.COUNTER.idFromName("global");
const stub = env.COUNTER.get(id);
return stub.fetch(request);
};
# Local development (requires Worker with DO)
npx wrangler pages dev ./dist --do=COUNTER=CounterDO@counter-worker
// functions/api/proxy.ts
interface Env {
AUTH_SERVICE: Fetcher;
}
export const onRequest: PagesFunction<Env> = async ({ env, request }) => {
// Call another Worker
return env.AUTH_SERVICE.fetch(request);
};
// functions/api/jobs.ts
interface Env {
MY_QUEUE: Queue;
}
export const onRequestPost: PagesFunction<Env> = async ({ env, request }) => {
const job = await request.json();
await env.MY_QUEUE.send({
type: "process",
data: job
});
return Response.json({ queued: true });
};
// functions/api/ai.ts
interface Env {
AI: Ai;
}
export const onRequestPost: PagesFunction<Env> = async ({ env, request }) => {
const { prompt } = await request.json();
const response = await env.AI.run("@cf/meta/llama-2-7b-chat-int8", {
messages: [{ role: "user", content: prompt }]
});
return Response.json(response);
};
# Apply to all paths
/*
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
# API CORS headers
/api/*
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type
# Cache static assets
/assets/*
Cache-Control: public, max-age=31536000, immutable
# Security headers for HTML
/*.html
Content-Security-Policy: default-src 'self'
X-XSS-Protection: 1; mode=block
# Remove header (prefix with !)
/public/*
! X-Robots-Tag
Note: Custom headers do NOT apply to Pages Functions responses.
# Simple redirect (302 default)
/home /
# Permanent redirect
/old-page /new-page 301
# External redirect
/twitter https://twitter.com/example
# Splat (wildcard)
/blog/* https://blog.example.com/:splat
# Placeholder
/users/:id /profiles/:id 301
# Proxy (200 status, relative URLs only)
/api/* /functions/api/:splat 200
# Force trailing slash
/about /about/ 301
Limits: 2,000 static + 100 dynamic redirects max.
{
"version": 1,
"include": ["/api/*", "/auth/*"],
"exclude": ["/api/health", "/static/*"]
}
exclude takes precedence over includeGitHub Setup:
GitLab Setup:
Branch Configuration:
main (configurable)# Create project
npx wrangler pages project create my-app
# List projects
npx wrangler pages project list
# Deploy production
npx wrangler pages deploy ./dist
# Deploy preview
npx wrangler pages deploy ./dist --branch=feature-x
# List deployments
npx wrangler pages deployment list --project-name=my-app
# Create deployment via API
curl -X POST \
"https://api.cloudflare.com/client/v4/accounts/{account_id}/pages/projects/{project_name}/deployments" \
-H "Authorization: Bearer {api_token}" \
-F "[email protected]" \
-F "file1=@dist/index.html"
# Basic development
npx wrangler pages dev ./dist
# With bindings
npx wrangler pages dev ./dist \
--kv=MY_KV \
--r2=BUCKET \
--d1=DB=<database-id> \
--port=8788
# With live reload (framework dev server)
npx wrangler pages dev -- npm run dev
# Proxy to another server
npx wrangler pages dev --proxy=3000
name = "my-pages-project"
pages_build_output_dir = "./dist"
# Compatibility
compatibility_date = "2024-01-01"
compatibility_flags = ["nodejs_compat"]
# Environment variables
[vars]
API_URL = "https://api.example.com"
# KV binding
[[kv_namespaces]]
binding = "MY_KV"
id = "abc123"
# R2 binding
[[r2_buckets]]
binding = "BUCKET"
bucket_name = "my-bucket"
# D1 binding
[[d1_databases]]
binding = "DB"
database_name = "my-db"
database_id = "xyz789"
# Durable Objects
[[durable_objects.bindings]]
name = "COUNTER"
class_name = "Counter"
script_name = "counter-worker"
# Service binding
[[services]]
binding = "AUTH"
service = "auth-worker"
Settings > Builds & deployments > Configure Preview deployments
Options:
# Include patterns
feat/* # Match feat/login, feat/dashboard
release-* # Match release-1.0, release-2.0
# Exclude patterns
dependabot/* # Skip dependency updates
wip/* # Skip work-in-progress
# Order: excludes evaluated first, then includes
Add to commit message:
git commit -m "Update docs [CI SKIP]"
git commit -m "Minor fix [skip ci]"
# List deployments
npx wrangler pages deployment list --project-name=my-app
# Rollback (redeploy a previous deployment)
# Note: Direct rollback command not available;
# redeploy the specific commit or use dashboard
Error: Build command failed
Solutions:
npm run buildError: Internal Server Error
Solutions:
wrangler pages devError: Page not found
Solutions:
_routes.json isn't excluding the path_redirects for conflictsError: Cannot read property of undefined
Solutions:
Possible causes:
my-app/
├── functions/ # Server-side code
│ ├── _middleware.ts # Global middleware
│ └── api/ # API routes
├── public/ # Static assets (copied to dist)
│ ├── _headers # Header rules
│ ├── _redirects # Redirect rules
│ └── robots.txt
├── src/ # Source code
├── dist/ # Build output (gitignored)
├── wrangler.toml # Wrangler config
└── package.json
_routes.json to skip Functions for static pathsxiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience