Cloudflare Pages

Web HostingJAMstackEdge ComputingWorkersCI/CDUnlimited Bandwidth

Web Hosting Platform

Cloudflare Pages

Overview

Cloudflare Pages is a JAMstack platform leveraging Cloudflare's global network. Offering ultra-fast delivery, unlimited free bandwidth, and powerful edge computing with Workers integration, it's rapidly expanding adoption due to unlimited free bandwidth. Advanced edge processing capabilities through Cloudflare Workers integration make it notable for cost efficiency.

Details

Launched in 2021, Cloudflare Pages is a static site hosting service backed by Cloudflare's powerful global infrastructure. Utilizing over 330 data centers distributed worldwide, it delivers content from the closest location to users. With unlimited bandwidth, fast builds, Git integration, and seamless integration with Cloudflare Workers, it enables dynamic functionality beyond traditional static site hosting. Particularly popular among developers who prioritize performance and cost efficiency, maintaining stable performance even with large-scale traffic.

Advantages and Disadvantages

Advantages

  • Unlimited Free Bandwidth: No additional charges regardless of traffic volume
  • Global High-Speed Delivery: Ultra-fast delivery from 330+ data centers
  • Workers Integration: Advanced server-side processing at the edge
  • Excellent Security: DDoS attack protection and Web Application Firewall
  • High Cost Performance: Enterprise-level features at low cost
  • Git Integration: Automatic integration with GitHub and GitLab
  • Fast Builds: Efficient build process and parallel processing

Disadvantages

  • New Platform: Limited features and information compared to competitors
  • High Learning Curve: Time required to understand Workers integration
  • Plugin Ecosystem: Fewer third-party integrations compared to Netlify or Vercel
  • Support: Limited support on free plan

Reference Pages

Code Examples

Basic Setup and Project Configuration

# Install Wrangler CLI
npm install -g wrangler@latest

# Login and authentication
wrangler login

# Create new Pages project
wrangler pages project create my-project

# Local development environment
wrangler pages dev ./dist
// wrangler.jsonc - Pages configuration
{
  "name": "my-pages-project",
  "compatibility_date": "2025-01-15",
  "pages_build_output_dir": "./dist",
  "functions": "./functions",
  "kv_namespaces": [
    {
      "binding": "MY_KV",
      "id": "your-kv-namespace-id",
      "preview_id": "your-preview-kv-namespace-id"
    }
  ],
  "vars": {
    "API_BASE_URL": "https://api.example.com"
  }
}

Static Site Deployment

# Direct deployment
wrangler pages deploy ./dist --project-name=my-project

# Git integration deployment (recommended)
# GitHub repository integration configured in dashboard

# Preview deployment
wrangler pages deploy ./dist --project-name=my-project --branch=feature/new-feature
# Deploy with GitHub Actions
name: Deploy to Cloudflare Pages
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      deployments: write
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build
        run: npm run build
      
      - name: Deploy to Cloudflare Pages
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          command: pages deploy dist --project-name=my-project

Framework Integration (Next.js, React, Vue)

// next.config.js - Cloudflare Pages support
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'export',
  trailingSlash: true,
  images: {
    unoptimized: true,
  },
  
  // Static export configuration
  experimental: {
    outputStandalone: true,
  },
};

module.exports = nextConfig;
# Nuxt.js project configuration
npx nuxi@latest init my-nuxt-app
cd my-nuxt-app

# nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    preset: 'cloudflare-pages',
  },
  experimental: {
    payloadExtraction: false,
  },
});

# Build and deploy
npm run build
wrangler pages deploy dist --project-name=my-nuxt-app
// vite.config.ts - Vite + Cloudflare Pages
import { defineConfig } from 'vite';
import { resolve } from 'path';

export default defineConfig({
  build: {
    outDir: 'dist',
    rollupOptions: {
      input: {
        main: resolve(__dirname, 'index.html'),
      },
    },
  },
  define: {
    __CLOUDFLARE_PAGES__: JSON.stringify(true),
  },
});

Custom Domains and SSL

# Add custom domain
wrangler pages domain add my-project example.com

# List domains
wrangler pages domain list

# DNS configuration (CNAME record)
# example.com -> your-project.pages.dev
// _headers file - Custom header configuration
/*
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: camera=(), microphone=(), geolocation=()

/api/*
  Access-Control-Allow-Origin: https://example.com
  Access-Control-Allow-Methods: GET, POST, PUT, DELETE
  Access-Control-Allow-Headers: Content-Type, Authorization

/static/*
  Cache-Control: public, max-age=31536000, immutable
// _redirects file - Redirect configuration
# 301 redirect
/old-page /new-page 301

# SPA fallback
/* /index.html 200

# API proxy
/api/* https://backend.example.com/api/:splat 200

# Conditional redirect
/blog/* /new-blog/:splat 301 Country=US

Serverless Functions and APIs

// functions/api/hello.ts - Pages Functions
interface Env {
  MY_KV: KVNamespace;
  DB: D1Database;
}

export const onRequest: PagesFunction<Env> = async (context) => {
  const { request, env, params } = context;
  const url = new URL(request.url);
  const name = url.searchParams.get('name') || 'World';
  
  // Save to KV storage
  await env.MY_KV.put(`greeting:${Date.now()}`, JSON.stringify({
    name,
    timestamp: new Date().toISOString(),
  }));
  
  return new Response(JSON.stringify({
    message: `Hello ${name}!`,
    timestamp: new Date().toISOString(),
    source: 'Cloudflare Pages Functions'
  }), {
    headers: {
      'Content-Type': 'application/json',
      'Cache-Control': 'public, max-age=60',
    },
  });
};
// functions/api/users/[id].ts - Dynamic routing
interface Env {
  DB: D1Database;
}

export const onRequestGet: PagesFunction<Env> = async (context) => {
  const { params, env } = context;
  const userId = params.id as string;
  
  try {
    const stmt = env.DB.prepare('SELECT * FROM users WHERE id = ?');
    const result = await stmt.bind(userId).first();
    
    if (!result) {
      return new Response('User not found', { status: 404 });
    }
    
    return Response.json(result);
  } catch (error) {
    return new Response('Database error', { status: 500 });
  }
};

export const onRequestPost: PagesFunction<Env> = async (context) => {
  const { request, env } = context;
  const userData = await request.json();
  
  try {
    const stmt = env.DB.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
    const result = await stmt.bind(userData.name, userData.email).run();
    
    return Response.json({ 
      id: result.meta.last_row_id,
      ...userData 
    }, { status: 201 });
  } catch (error) {
    return new Response('Database error', { status: 500 });
  }
};

CI/CD and Production Optimization

// functions/_middleware.ts - Middleware
interface Env {
  RATE_LIMITER: KVNamespace;
}

export const onRequest: PagesFunction<Env> = async (context) => {
  const { request, env, next } = context;
  const clientIP = request.headers.get('CF-Connecting-IP') || 'unknown';
  
  // Rate limiting
  const key = `rate_limit:${clientIP}`;
  const current = await env.RATE_LIMITER.get(key);
  const count = parseInt(current || '0');
  
  if (count > 100) {
    return new Response('Rate limit exceeded', { status: 429 });
  }
  
  await env.RATE_LIMITER.put(key, (count + 1).toString(), {
    expirationTtl: 3600, // 1 hour
  });
  
  // Add security headers
  const response = await next();
  const newResponse = new Response(response.body, response);
  
  newResponse.headers.set('X-Frame-Options', 'DENY');
  newResponse.headers.set('X-Content-Type-Options', 'nosniff');
  newResponse.headers.set('Strict-Transport-Security', 'max-age=31536000');
  
  return newResponse;
};
# Wrangler command collection
wrangler pages project list           # List projects
wrangler pages deployment list        # Deployment history
wrangler pages download               # Download site
wrangler pages deployment tail        # Real-time logs

# KV operations
wrangler kv:namespace create "MY_KV"  # Create KV namespace
wrangler kv:key put "key" "value"     # Save data
wrangler kv:key get "key"             # Get data

# D1 database operations
wrangler d1 create my-database        # Create D1 database
wrangler d1 execute my-database --file=schema.sql  # Execute SQL file

# Check analytics
wrangler pages deployment view --project-name=my-project
// wrangler.jsonc - Advanced configuration
{
  "name": "my-advanced-project",
  "compatibility_date": "2025-01-15",
  "pages_build_output_dir": "./dist",
  "functions": "./functions",
  
  // KV Namespaces
  "kv_namespaces": [
    {
      "binding": "CACHE",
      "id": "cache-namespace-id",
      "preview_id": "cache-preview-namespace-id"
    }
  ],
  
  // D1 Databases
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "my-database",
      "database_id": "database-id"
    }
  ],
  
  // Environment Variables
  "vars": {
    "ENVIRONMENT": "production",
    "API_VERSION": "v1"
  },
  
  // Secrets (use wrangler secret put)
  // "secrets": ["DATABASE_URL", "JWT_SECRET"]
}