<< All versions

Skill v1.0.1

currentAutomated scan100/100
sickn33/agentic-awesome-skills/cloudflare-pages

+4 new

──Details
PublishedOctober 29, 2026 at 08:00 AM
Content Hashsha256:bb160343f8438696...
Git SHAe622955f98ba
Bump Typepatch
Compare with v1.0.0
──Files
Files (1 file, 10.4 KB)
SKILL.md10.4 KBactive
SKILL.md · 323 lines · 10.4 KB

name: cloudflare-pages description: Deploy static sites and full-stack apps on Cloudflare Pages with previews, functions, and custom domains. category: devops risk: critical source: https://github.com/BagelHole/DevOps-Security-Agent-Skills source_repo: BagelHole/DevOps-Security-Agent-Skills source_type: community date_added: '2026-09-20' license: MIT license_source: https://github.com/BagelHole/DevOps-Security-Agent-Skills/blob/main/LICENSE compatibility: Requires the relevant OS/platform tooling and privileged access where noted. Docs-only; helper scripts and templates not bundled. metadata: author: devops-skills version: '1.0'


Cloudflare Pages

Deploy frontend projects with preview builds, edge functions, and global CDN delivery on Cloudflare's network.

When to Use

  • Deploying static sites (React, Vue, Astro, Hugo, Next.js static export).
  • Full-stack applications using Pages Functions for server-side logic.
  • Projects that need automatic preview deployments per pull request.
  • Teams that want zero-config CDN with custom domain and TLS.
  • Migrating from Vercel, Netlify, or GitHub Pages to Cloudflare's ecosystem.

Prerequisites

Pin a supported Wrangler/Node version; framework adapters and provider limits change. Full-stack Next.js requires the current supported Workers/OpenNext workflow rather than assuming the legacy next-on-pages adapter. Confirm account/project/environment before deploy/delete/domain changes; previews can expose private builds and need access control. _headers applies to static assets, not Functions responses: set security/CORS headers in Functions and implement OPTIONS as needed. Bindings/cache keys must be scoped to authorized users/tenants when data is private; the shared key sample is only for public data. The CI example needs trusted-branch secret boundaries and an explicit preview branch; do not deploy untrusted PR builds with production credentials.

  • Node.js 18+ and npm installed locally.
  • A Cloudflare account (free tier works for most projects).
  • Wrangler CLI installed: npm install -g wrangler.
  • Authenticated via wrangler login or CLOUDFLARE_API_TOKEN environment variable.
  • Source code in a Git repository (GitHub or GitLab for dashboard integration).

Project Setup via Wrangler

Create a New Project

bash
# Create a new Pages project
npx wrangler pages project create my-site
# List existing projects
npx wrangler pages project list
# Delete a project (removes all deployments)
npx wrangler pages project delete my-site

Deploy from Local Build Output

bash
# Build your framework first
npm run build
# Deploy the output directory
npx wrangler pages deploy dist --project-name=my-site
# Deploy with a custom branch name (triggers preview URL)
npx wrangler pages deploy dist --project-name=my-site --branch=feature-auth
# Deploy and inspect the actual result (tailing text is not JSON parsing)
npx wrangler pages deploy dist --project-name=my-site --branch=main 2>&1 | tail -1

List and Manage Deployments

bash
# List recent deployments
npx wrangler pages deployment list --project-name=my-site
# Tail live logs from a deployment
npx wrangler pages deployment tail --project-name=my-site --environment=production

Dashboard Git Integration

  1. Navigate to Workers & Pages > Create application > Pages.
  2. Connect your GitHub or GitLab account.
  3. Select the repository and configure:
  • Production branch: main
  • Build command: npm run build
  • Build output directory: dist (or build, .next, public depending on framework)
  1. Set environment variables per environment (Production vs Preview).

Framework Presets

Cloudflare auto-detects frameworks. Override if needed:

FrameworkBuild CommandOutput Directory
React CRAnpm run buildbuild
Vitenpm run builddist
Next.js static exportnpm run build with export configurationout
Astronpm run builddist
Hugohugopublic
SvelteKitnpm run build.svelte-kit/cloudflare

Preview Deployments

Every non-production branch gets a unique preview URL automatically.

# URL format for preview deployments
https://<commit-hash>.<project-name>.pages.dev
https://<branch-name>.<project-name>.pages.dev

Branch-Based Access Control

bash
# Set preview branch patterns in wrangler.toml (Pages-specific)
# Or configure via dashboard: Settings > Builds & deployments
# Include branches: feature/*, staging
# Exclude branches: dependabot/*

Preview Comment on Pull Requests

Enable the Cloudflare Pages GitHub App to post deployment URLs as PR comments. Configure under Settings > Builds & deployments > Preview comment.

Pages Functions

Pages Functions provide server-side logic deployed alongside your static site. Place files in a functions/ directory at the project root.

Basic API Route

typescript
// functions/api/hello.ts
export const onRequestGet: PagesFunction = async (context) => {
return new Response(JSON.stringify({ message: "Hello from the edge" }), {
headers: { "Content-Type": "application/json" },
});
};
// functions/api/users/[id].ts — dynamic route parameter
export const onRequestGet: PagesFunction = async (context) => {
const userId = context.params.id;
return new Response(JSON.stringify({ userId }), {
headers: { "Content-Type": "application/json" },
});
};

Middleware

typescript
// functions/_middleware.ts — runs before all routes
export const onRequest: PagesFunction = async (context) => {
const authHeader = context.request.headers.get("Authorization");
// validateBearerToken is application code: verify signature/issuer/audience/expiry
// or use Cloudflare Access validation; header presence is not authentication.
if (!authHeader || !(await validateBearerToken(authHeader, context.env))) {
return new Response("Unauthorized", { status: 401 });
}
return context.next();
};

Functions with Bindings

typescript
// functions/api/data.ts — using KV and D1 bindings
interface Env {
MY_KV: KVNamespace;
MY_DB: D1Database;
MY_BUCKET: R2Bucket;
}
export const onRequestGet: PagesFunction<Env> = async (context) => {
// Read from KV
const cached = await context.env.MY_KV.get("key");
if (cached) return new Response(cached);
// Query D1
const result = await context.env.MY_DB.prepare(
"SELECT * FROM items LIMIT 10"
).all();
// Cache in KV
await context.env.MY_KV.put("key", JSON.stringify(result.results), {
expirationTtl: 300,
});
return Response.json(result.results);
};

Wrangler Configuration

toml
# wrangler.toml — Pages project configuration
name = "my-site"
compatibility_date = "2024-09-01"
pages_build_output_dir = "dist"
# KV namespace binding
[[kv_namespaces]]
binding = "MY_KV"
id = "abc123def456"
# D1 database binding
[[d1_databases]]
binding = "MY_DB"
database_name = "my-app-db"
database_id = "xxxx-yyyy-zzzz"
# R2 bucket binding
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "app-assets"
# Environment variables
[vars]
API_BASE_URL = "https://api.example.com"

Headers and Redirects

Custom Headers

# public/_headers
/assets/*
Cache-Control: public, max-age=31536000, immutable
/*
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'
/api/*
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, OPTIONS

Redirects

# public/_redirects
/old-page /new-page 301
/blog/:slug /posts/:slug 301
/docs/* https://docs.example.com/:splat 302
/home / 302

Custom Domains

bash
# Add a custom domain via Cloudflare dashboard:
# Pages project > Custom domains > Set up a custom domain
# Or via API
curl -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/pages/projects/my-site/domains" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"www.example.com"}'

CI/CD Integration

GitHub Actions

yaml
# .github/workflows/deploy.yml
name: Deploy to Cloudflare Pages
on:
push:
branches: [main]
pull_request:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build
- uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy dist --project-name=my-site

Troubleshooting

SymptomCauseFix
Build fails with out-of-memoryInspect current platform limit and logsReduce build memory or use a supported build environment
Functions return 404functions/ directory not at project rootMove functions/ to repo root, not inside src/
Preview URL shows old contentBrowser cache or stale deploymentHard refresh; check deployment list for latest commit hash
Custom domain shows SSL errorDNS/domain activation or certificate issueVerify Pages domain status and official DNS requirements before changing records
_headers file ignoredFile not in build output directoryPlace in public/ so it copies to dist/ during build
Bindings undefined in FunctionsMissing wrangler.toml or dashboard configAdd bindings in wrangler.toml and redeploy
Function size limit exceededBundle exceeds current plan limitInspect bundle and supported limits; storing libraries remotely is not an automatic runtime fix

Related Skills

  • cloudflare-workers (cloudflare-workers) - Edge backend logic and API routes
  • cloudflare-r2 (cloudflare-r2) - Object storage for assets and uploads
  • cloudflare-zero-trust (cloudflare-zero-trust) - Protect preview deployments with Access policies
  • cdn-setup (cdn-setup) - General CDN configuration patterns

Limitations

  • Infrastructure commands can disrupt services: confirm target host/scope and have backups/snapshots before mutating state.
  • Docs-only import: upstream scripts and templates not bundled.
← v1.0.0All versions