Cloudflare Pages — Free Full-Stack Deployment on the Global Edge
Posted on: 4/26/2026 11:16:06 PM
Table of contents
- 1. What Is Cloudflare Pages?
- 2. Three Ways to Deploy to Cloudflare Pages
- 3. Pages Functions — Turning Static Sites into Full-Stack Apps
- 4. Bindings — Connecting the Cloudflare Ecosystem
- 5. Preview Deployments and Rollbacks
- 6. Cloudflare Pages vs Vercel vs Netlify
- 7. Deploying Vue.js to Cloudflare Pages — Practical Guide
- 8. Free Tier Limits in Detail
- 9. Performance Optimization on Cloudflare Pages
- 10. CI/CD Workflow for Teams
- 11. When NOT to Use Cloudflare Pages
- Conclusion
Have you ever wanted to deploy a full-stack web application for absolutely free, with no server management, and have it automatically distributed across 330+ data centers worldwide? Cloudflare Pages makes that a reality. It's not just a static hosting platform — with Pages Functions, bindings to KV, D1, R2, and even Workers AI — Cloudflare Pages has become a complete full-stack serverless platform running on the edge.
1. What Is Cloudflare Pages?
Cloudflare Pages is a JAMstack platform that lets you deploy web applications directly onto Cloudflare's global edge network. Unlike traditional hosting that places your server in a single region, Pages distributes content to the node closest to each user — minimizing latency to near-zero.
The standout feature: Cloudflare Pages offers unlimited bandwidth on the free tier. Compared to Vercel (100GB/month) or Netlify (100GB/month), this is a massive advantage for high-traffic websites.
graph TB
A["Developer pushes code"] --> B["Git Provider
(GitHub/GitLab)"]
B --> C["Cloudflare Build Pipeline"]
C --> D["Static Assets"]
C --> E["Pages Functions"]
D --> F["CDN Edge
330+ PoPs"]
E --> G["Workers Runtime
Edge Compute"]
F --> H["Global Users"]
G --> H
style A fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style B fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style C fill:#e94560,stroke:#fff,color:#fff
style D fill:#2c3e50,stroke:#fff,color:#fff
style E fill:#2c3e50,stroke:#fff,color:#fff
style F fill:#4CAF50,stroke:#fff,color:#fff
style G fill:#4CAF50,stroke:#fff,color:#fff
style H fill:#e94560,stroke:#fff,color:#fff
Figure 1: Deployment and content distribution flow on Cloudflare Pages
2. Three Ways to Deploy to Cloudflare Pages
2.1. Git Integration (Recommended)
Connect your repository from GitHub or GitLab. Every time you push code, Cloudflare automatically builds and deploys. The main branch deploys to production, while other branches create separate preview deployments with unique URLs.
# Project structure: Vue.js + Pages Functions
my-app/
├── src/ # Frontend source
�� ├── App.vue
│ └── main.ts
├── functions/ # Pages Functions (server-side)
│ ├── api/
│ │ ├── hello.ts # GET /api/hello
│ │ └── users/
│ │ └── [id].ts # GET /api/users/:id
│ └── _middleware.ts # Global middleware
├── public/
├── package.json
└── wrangler.toml # Cloudflare configuration
2.2. Direct Upload
Upload pre-built directories via Dashboard or Wrangler CLI. Ideal when you use a separate CI/CD pipeline (GitHub Actions, GitLab CI) and want full control over the build process.
# Upload via Wrangler CLI
npx wrangler pages deploy ./dist --project-name=my-app
# Or create a new project and deploy
npx wrangler pages project create my-app
npx wrangler pages deploy ./dist --project-name=my-app --branch=main
2.3. C3 (Create Cloudflare CLI)
The official scaffolding tool that auto-detects frameworks and configures builds:
# Create a new project with your framework of choice
npm create cloudflare@latest my-app
# Choose framework: Vue, React, Nuxt, Astro, SvelteKit, Remix...
# C3 auto-configures wrangler.toml and deploy commands
3. Pages Functions — Turning Static Sites into Full-Stack Apps
Pages Functions run on the Cloudflare Workers runtime using file-based routing similar to Next.js or Nuxt. Each file in the functions/ directory maps to an API endpoint.
Advantages over traditional serverless
Pages Functions start in milliseconds (no cold start like AWS Lambda). Code runs on V8 isolates, not containers — lighter, faster, and more resource-efficient.
3.1. File-based Routing
| File path | Route | Notes |
|---|---|---|
functions/api/hello.ts | /api/hello | Static route |
functions/api/users/[id].ts | /api/users/:id | Dynamic param |
functions/api/posts/[[path]].ts | /api/posts/* | Catch-all route |
functions/_middleware.ts | All routes | Global middleware |
3.2. Writing API Endpoints
// functions/api/hello.ts
export const onRequestGet: PagesFunction = async (context) => {
return Response.json({
message: "Hello from Cloudflare Edge!",
timestamp: new Date().toISOString(),
colo: context.request.cf?.colo, // Nearest data center
});
};
// functions/api/users/[id].ts
interface Env {
DB: D1Database; // Binding to D1
}
export const onRequestGet: PagesFunction<Env> = async (context) => {
const userId = context.params.id;
const user = await context.env.DB
.prepare("SELECT * FROM users WHERE id = ?")
.bind(userId)
.first();
if (!user) {
return new Response("Not found", { status: 404 });
}
return Response.json(user);
};
3.3. Middleware — Authentication and Logging
// functions/_middleware.ts
export const onRequest: PagesFunction = async (context) => {
const start = Date.now();
const response = await context.next();
response.headers.set("Access-Control-Allow-Origin", "*");
response.headers.set("X-Response-Time", `${Date.now() - start}ms`);
return response;
};
// functions/api/_middleware.ts — applies only to /api/*
export const onRequest: PagesFunction = async (context) => {
const authHeader = context.request.headers.get("Authorization");
if (!authHeader?.startsWith("Bearer ")) {
return new Response("Unauthorized", { status: 401 });
}
context.data.user = await verifyToken(authHeader.slice(7));
return context.next();
};
4. Bindings — Connecting the Cloudflare Ecosystem
The real power of Pages Functions lies in bindings — direct connections to Cloudflare services without making network API calls. Data flows within the same data center, with near-zero latency.
graph LR
PF["Pages Functions"] --> KV["Workers KV
Key-Value Store"]
PF --> D1["D1 Database
Serverless SQLite"]
PF --> R2["R2 Storage
Object Storage"]
PF --> DO["Durable Objects
Stateful Edge"]
PF --> AI["Workers AI
ML Inference"]
PF --> Q["Queues
Message Queue"]
style PF fill:#e94560,stroke:#fff,color:#fff
style KV fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style D1 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style R2 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style DO fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style AI fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style Q fill:#f8f9fa,stroke:#e94560,color:#2c3e50
Figure 2: Bindings connecting Pages Functions to the Cloudflare ecosystem
4.1. KV — Ultra-Fast Key-Value Storage
Workers KV is a distributed key-value store spanning the entire edge network. Perfect for caching, configuration, and session data with sub-10ms reads at the edge.
// wrangler.toml
[[kv_namespaces]]
binding = "CACHE"
id = "xxxx"
// functions/api/config.ts
interface Env {
CACHE: KVNamespace;
}
export const onRequestGet: PagesFunction<Env> = async (context) => {
let config = await context.env.CACHE.get("site-config", "json");
if (!config) {
config = await fetchConfigFromOrigin();
await context.env.CACHE.put("site-config", JSON.stringify(config), {
expirationTtl: 3600,
});
}
return Response.json(config);
};
4.2. D1 — SQL Database on the Edge
D1 is a serverless SQLite database running at the edge. The free tier allows 5 million reads/day and 100K writes/day — more than enough for most personal projects and early-stage startups.
# Create database and run migrations
npx wrangler d1 create my-db
npx wrangler d1 execute my-db --file=./schema.sql
-- schema.sql
CREATE TABLE posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
slug TEXT UNIQUE NOT NULL,
content TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_posts_slug ON posts(slug);
4.3. R2 — Object Storage with Zero Egress Fees
R2 is S3-compatible but charges zero egress fees. Free tier: 10GB storage, 10 million reads/month. Ideal for image hosting, file uploads, and static assets.
// functions/api/upload.ts
interface Env {
ASSETS: R2Bucket;
}
export const onRequestPost: PagesFunction<Env> = async (context) => {
const formData = await context.request.formData();
const file = formData.get("file") as File;
if (!file) {
return new Response("No file uploaded", { status: 400 });
}
const key = `uploads/${Date.now()}-${file.name}`;
await context.env.ASSETS.put(key, file.stream(), {
httpMetadata: { contentType: file.type },
});
return Response.json({ url: `/cdn/${key}`, size: file.size });
};
4.4. Workers AI — ML Inference at the Edge
Run AI models directly on Cloudflare infrastructure — text generation, image classification, embeddings, translation — no separate GPU servers needed.
// functions/api/summarize.ts
interface Env {
AI: Ai;
}
export const onRequestPost: PagesFunction<Env> = async (context) => {
const { text } = await context.request.json();
const result = await context.env.AI.run(
"@cf/meta/llama-3.1-8b-instruct",
{
messages: [
{ role: "system", content: "Summarize the following text concisely in 3 sentences." },
{ role: "user", content: text },
],
}
);
return Response.json({ summary: result.response });
};
5. Preview Deployments and Rollbacks
Every pull request automatically receives a unique preview URL (e.g., abc123.my-app.pages.dev). Reviewers can access the preview directly to verify changes without affecting production.
Instant Rollbacks
Discovered a bug after deploying? Just one click on the Dashboard rolls back to any previous deployment. No need to revert code or rebuild — the previous version is activated instantly since every deployment is already cached on the edge.
6. Cloudflare Pages vs Vercel vs Netlify
| Criteria | Cloudflare Pages | Vercel | Netlify |
|---|---|---|---|
| Bandwidth (Free) | Unlimited | 100 GB/month | 100 GB/month |
| Builds/month (Free) | 500 | 6,000 | 300 minutes |
| Serverless Functions | 100K req/day | 100K req/month | 125K req/month |
| Edge Runtime | V8 Isolate (everywhere) | Edge + Serverless | Edge Functions (Deno) |
| Integrated Database | D1 (SQLite), 5GB free | Postgres (Neon) | None |
| Object Storage | R2 (10GB, zero egress) | Blob Storage | Blobs |
| Cold Start | ~0ms (V8 isolate) | ~250ms (Lambda) | ~200ms |
| Custom Domains (Free) | 100/project | 50/project | Unlimited |
| DDoS Protection | Built-in, free, unlimited | Basic | Basic |
When to choose Cloudflare Pages?
Choose Pages when you need unlimited bandwidth, low global latency, and zero cold start. Choose Vercel for Next.js with complex ISR/SSR. Choose Netlify for built-in CMS integration and form handling.
7. Deploying Vue.js to Cloudflare Pages — Practical Guide
End-to-end workflow for deploying a Vue.js app with API backend on Pages Functions:
# Step 1: Create Vue + Cloudflare project
npm create cloudflare@latest my-vue-app -- --framework=vue
# Step 2: Add Pages Functions
mkdir -p functions/api
# Step 3: Configure wrangler.toml
name = "my-vue-app"
compatibility_date = "2026-04-01"
pages_build_output_dir = "./dist"
[[d1_databases]]
binding = "DB"
database_name = "my-db"
database_id = "your-database-id"
[[kv_namespaces]]
binding = "CACHE"
id = "your-kv-id"
# Step 4: Local development
npx wrangler pages dev -- npx vite
# Step 5: Deploy
npx wrangler pages deploy ./dist
Configure proxy for local development in vite.config.ts:
// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
server: {
proxy: {
'/api': {
target: 'http://localhost:8788',
changeOrigin: true,
},
},
},
});
8. Free Tier Limits in Detail
| Service | Free Tier | Notes |
|---|---|---|
| Pages Hosting | Unlimited bandwidth, 500 builds/month | No credit card required |
| Pages Functions | 100,000 requests/day | Runs on Workers runtime |
| Workers KV | 100K reads/day, 1K writes/day, 1GB | Eventually consistent |
| D1 Database | 5M reads/day, 100K writes/day, 5GB | Serverless SQLite |
| R2 Storage | 10GB, 10M reads/month, 1M writes/month | S3-compatible, zero egress |
| Workers AI | 10,000 neurons/day | Many free models available |
| Queues | 1M operations/month | Message queue |
Important Limits to Note
Max file size per asset: 25 MB. Max files per site: 20,000 (free) or 100,000 (paid). Build timeout: 20 minutes. Workers CPU time limited to 10ms (free) or 30 seconds (paid) per request.
9. Performance Optimization on Cloudflare Pages
9.1. Cache Control for Static Assets
// functions/_middleware.ts
export const onRequest: PagesFunction = async (context) => {
const response = await context.next();
const url = new URL(context.request.url);
// Immutable cache for content-hashed assets
if (url.pathname.match(/\/assets\/.*\.[a-f0-9]{8}\./)) {
response.headers.set(
"Cache-Control",
"public, max-age=31536000, immutable"
);
}
return response;
};
9.2. Smart Placement
By default, Functions run at the edge closest to the user. If a function needs to query a database in a specific region, Smart Placement automatically moves the function closer to the data source to reduce round-trips.
# wrangler.toml
[placement]
mode = "smart"
9.3. Automatic CDN Integration
When you add a custom domain, your website automatically receives full Cloudflare features:
- HTTP/3 + QUIC — the fastest transport protocol available
- Brotli compression — 20-30% smaller than Gzip
- Early Hints (103) — browser starts loading resources before the main response arrives
- DDoS Protection — free, unlimited, automatic
- SSL/TLS — free HTTPS for every custom domain
- Web Analytics — privacy-first analytics without client-side scripts
10. CI/CD Workflow for Teams
sequenceDiagram
participant Dev as Developer
participant GH as GitHub
participant CF as Cloudflare Pages
participant Edge as Edge Network
Dev->>GH: Push feature branch
GH->>CF: Webhook trigger
CF->>CF: Build & test
CF->>Edge: Deploy preview
CF-->>GH: Comment preview URL
Note over Dev,GH: Code review on preview URL
Dev->>GH: Merge to main
GH->>CF: Webhook trigger
CF->>CF: Build production
CF->>Edge: Deploy globally
CF-->>Dev: Deploy notification
Figure 3: Automated CI/CD workflow with Git integration
11. When NOT to Use Cloudflare Pages
Pages works great for most web apps, but there are cases where alternatives may be better:
- Long-running processes — Workers CPU time is limited to 30 seconds (paid). Heavy processing should use Queues + Workers or switch to containers (Fly.io, Railway).
- Complex relational databases — D1 is SQLite and doesn't support complex stored procedures or multi-master replication. For full PostgreSQL/MySQL, combine Pages with an external database (Neon, PlanetScale).
- Full Next.js App Router — Some ISR on-demand features aren't fully supported on Pages yet. Vercel remains the best choice for Next.js.
- Persistent WebSockets — Pages Functions are stateless. WebSockets require Durable Objects combined with Workers.
Conclusion
Cloudflare Pages has evolved far beyond its "static site hosting" origins. With Pages Functions, bindings to D1/KV/R2/AI, and a network spanning 330+ data centers — it's the most powerful free full-stack serverless platform available today. Unlimited bandwidth, zero cold starts, and deep integration with Cloudflare's security ecosystem (WAF, DDoS, Bot Management) make it a top choice for individual developers, startups, and even large-scale production deployments.
If you're looking for a place to deploy your Vue.js, Nuxt, Astro, or any framework application without worrying about bandwidth costs, Cloudflare Pages deserves to be your first choice.
References
ClickHouse 26.x — Columnar Database for Billion-Row Real-Time Analytics
Drizzle ORM — The Lightweight TypeScript ORM Reshaping How We Write SQL
Disclaimer: The opinions expressed in this blog are solely my own and do not reflect the views or opinions of my employer or any affiliated organizations. The content provided is for informational and educational purposes only and should not be taken as professional advice. While I strive to provide accurate and up-to-date information, I make no warranties or guarantees about the completeness, reliability, or accuracy of the content. Readers are encouraged to verify the information and seek independent advice as needed. I disclaim any liability for decisions or actions taken based on the content of this blog.