cloudflare-pages

Cloudflare Pages Skill

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "cloudflare-pages" with this command: npx skills add serendipityoneinc/srp-claude-code-marketplace/serendipityoneinc-srp-claude-code-marketplace-cloudflare-pages

Cloudflare Pages Skill

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.

When to Use This Skill

  • Deploying static sites or JAMstack applications

  • Building full-stack apps with serverless functions

  • Configuring Pages Functions with bindings (KV, R2, D1)

  • Setting up Git-based CI/CD with preview deployments

  • Configuring headers, redirects, and routing

  • Troubleshooting build or deployment issues

  • Migrating from other hosting platforms to Pages

When NOT to Use This Skill

  • For Cloudflare Workers alone (use workers skill)

  • For general Cloudflare CDN/DNS configuration

  • For non-web applications or APIs without UI

  • For container-based deployments (Pages is serverless)

Core Concepts

Architecture Overview

┌─────────────────────────────────────────────────────────────────┐ │ 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 │ └───────────────┘ └───────────────┘ └───────────────┘

Platform Limits

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

Configuration Limits

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)

Quick Start

Deploy with Git Integration

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

Deploy with Wrangler CLI

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

Deploy via Dashboard

  • Go to Workers & Pages > Create application

  • Click Get started under Pages

  • Drag and drop your build folder or zip file

  • Enter project name and deploy

Build Configuration

Framework Presets

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

System Environment Variables

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

Custom Environment Variables

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

Monorepo Configuration

my-monorepo/ ├── apps/ │ └── web/ # Root directory: apps/web │ ├── src/ │ └── package.json └── packages/

Set Root directory to apps/web in build settings.

Pages Functions

Directory Structure

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

Basic Function

// 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 }); };

Dynamic Routes

// 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("/")}); };

Middleware

// 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(); }

TypeScript Support

// 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 }); };

Bindings

KV Namespace

// 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

R2 Bucket

// 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

D1 Database

// 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>

Durable Objects

// 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

Service Bindings

// 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); };

Queue Producer

// 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 }); };

Workers AI

// 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); };

Static Configuration

Headers (_headers)

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.

Redirects (_redirects)

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.

Routes (_routes.json)

{ "version": 1, "include": ["/api/", "/auth/"], "exclude": ["/api/health", "/static/*"] }

  • Controls which paths invoke Functions

  • exclude takes precedence over include

  • Paths not matching either are served as static assets

  • Max 100 rules combined

Deployment Methods

Git Integration (Recommended)

GitHub Setup:

  • Workers & Pages > Create application > Connect to Git

  • Select GitHub and authorize

  • Choose repository

  • Configure build settings

  • Deploy

GitLab Setup:

  • Requires Maintainer role or higher

  • Grants access to all repositories on account

Branch Configuration:

  • Production branch: main (configurable)

  • Preview branches: All non-production (configurable)

  • Custom rules: Include/exclude patterns

Direct Upload (Wrangler)

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

API Deployment

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 "manifest=@manifest.json"
-F "file1=@dist/index.html"

Local Development

Wrangler Dev Server

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

wrangler.toml Configuration

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"

Branch Build Controls

Preview Deployments

Settings > Builds & deployments > Configure Preview deployments

Options:

  • All non-production branches: Default, deploys everything

  • None: Disables preview builds

  • Custom branches: Selective deployment

Custom Branch Rules

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

Skip Builds

Add to commit message:

git commit -m "Update docs [CI SKIP]" git commit -m "Minor fix [skip ci]"

Rollbacks

Via Dashboard

  • Go to project > Deployments

  • Find the deployment to rollback to

  • Click three dots > Rollback to this deployment

Via Wrangler

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

Troubleshooting

Build Failures

Error: Build command failed

Solutions:

  • Check build logs for specific error

  • Verify build command and output directory

  • Test locally: npm run build

  • Check Node.js version compatibility

  • Review environment variables

Function 500 Errors

Error: Internal Server Error

Solutions:

  • Check function logs in dashboard

  • Verify bindings are configured

  • Test locally with wrangler pages dev

  • Check for unhandled promise rejections

Static Asset 404

Error: Page not found

Solutions:

  • Verify build output directory is correct

  • Check _routes.json isn't excluding the path

  • Confirm file exists in build output

  • Review _redirects for conflicts

Binding Not Found

Error: Cannot read property of undefined

Solutions:

  • Verify binding is configured in dashboard

  • Check binding name matches code

  • For local dev, pass binding flags to wrangler

  • Verify wrangler.toml configuration

Headers/Redirects Not Working

Possible causes:

  • File not in correct location (must be in build output)

  • Syntax errors in file

  • For Functions responses, use code instead

  • Exceeded limit (100 headers, 2100 redirects)

Best Practices

Project Structure

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

Security Recommendations

  • Use environment variables for secrets

  • Implement CORS properly for APIs

  • Add security headers (CSP, X-Frame-Options)

  • Validate and sanitize user input

  • Use HTTPS-only custom domains

  • Review function permissions and bindings

Performance Tips

  • Use _routes.json to skip Functions for static paths

  • Set appropriate Cache-Control headers

  • Optimize images before upload

  • Use code splitting for large apps

  • Leverage Cloudflare's global CDN

Resources

Official Documentation

  • Pages Documentation

  • Functions Guide

  • Framework Guides

  • Platform Limits

Tools

  • Wrangler CLI

  • C3 (Create Cloudflare)

Related Services

  • Workers

  • KV

  • R2

  • D1

  • Durable Objects

Community

  • Discord

  • Community Forum

  • @CloudflareDev

Version History

  • 1.0.0 (2026-01-13): Initial skill release

  • Complete Pages platform documentation

  • Static site and full-stack deployment guides

  • Pages Functions with routing and middleware

  • All bindings (KV, R2, D1, DO, Queues, AI)

  • Build configuration and framework presets

  • Headers, redirects, and routes configuration

  • Git integration and direct upload methods

  • Local development with Wrangler

  • Branch build controls and rollbacks

  • Troubleshooting guide

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

lark-docs

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

slurm

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

lark-messages

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

mac-setup

No summary provided by upstream source.

Repository SourceNeeds Review