WRK
vs
PGS
Workers vs Pages
api • deploy • edge fn

Cloudflare Workers vs Pages: when to use each

Compare Workers and Pages: serverless functions, static hosting, Pages Functions, use cases, and how to choose for each project.

10 min read17/06/2026

Quick verdict

  • Use Workers when you need edge logic: API backend, proxy, auth, middleware, job handler — no static file serving needed
  • Use Pages when you have a frontend: Next.js, Astro, SvelteKit, React SPA — Pages handles both hosting and server-side code (Pages Functions run on the Workers runtime under the hood)
  • Not an either/or choice: many projects use Pages for the frontend and a separate Worker for the backend API, both binding to the same KV/D1/R2

Quick comparison

Workers Pages
Primary purpose Serverless function at the edge Static hosting + Functions
How to deploy wrangler deploy Git push (or wrangler pages deploy)
Serve static files No (needs R2 or KV) Yes — automatically from build output
Server-side logic The whole thing is logic Pages Functions (Workers runtime underneath)
Framework support None built-in Next.js, Astro, SvelteKit, Remix...
Bind KV/D1/R2/Queue Yes Yes (inside Pages Functions)
Cron triggers Yes ([triggers] in wrangler.toml) No
Free tier requests 100,000 req/day Unlimited (static files, no cap)
Free tier builds Not applicable 500 builds/month
Custom domain Yes Yes
Preview deployments No Yes (each branch/PR gets a preview URL)

What Workers is and what it does

Workers is Cloudflare's serverless function platform, running at the edge — your code runs in a Cloudflare datacenter near the user, not on a fixed server.

Each Worker receives an HTTP request, processes it, and returns a response. No server, no container, no meaningful cold start.

A basic Worker looks like this

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === "/api/ping") {
      return Response.json({ ok: true, ts: Date.now() });
    }

    return new Response("Not found", { status: 404 });
  },
};

Deploy with:

wrangler deploy

What Workers is good for

Pure API backend

When a mobile app or SPA needs a lightweight API without standing up a dedicated server:

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method === "POST" && new URL(request.url).pathname === "/api/contact") {
      const body = await request.json<{ email: string; message: string }>();
      await env.DB.prepare("INSERT INTO contacts (email, message) VALUES (?, ?)")
        .bind(body.email, body.message)
        .run();
      return Response.json({ ok: true });
    }
    return new Response("Not found", { status: 404 });
  },
};

Proxy and middleware

Intercept requests before they reach the origin: validate tokens, rate limit, rewrite URLs, inject headers:

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const token = request.headers.get("Authorization");
    if (!token || !(await verify(token, env.JWT_SECRET))) {
      return new Response("Unauthorized", { status: 401 });
    }
    return fetch(request); // forward to origin
  },
};

Edge cron jobs

Workers can run on a schedule without any incoming request:

# wrangler.toml
[triggers]
crons = ["0 * * * *"] # every hour
export default {
  async scheduled(event: ScheduledEvent, env: Env): Promise<void> {
    await syncDataFromExternalApi(env);
  },
};

Pages Functions don't support cron triggers.

Queue consumer

Process jobs from Cloudflare Queues:

export default {
  async queue(batch: MessageBatch<Job>, env: Env): Promise<void> {
    for (const msg of batch.messages) {
      await processJob(msg.body, env);
      msg.ack();
    }
  },
};

What Pages is and what it does

Pages is Cloudflare's static hosting platform. Connect a GitHub/GitLab repo, and every push triggers an automatic build and deploy to a global CDN.

The key detail: Pages isn't just for serving HTML/CSS/JS — it has Pages Functions, which let you write server-side logic directly inside the project, with the same ability to bind KV/D1/R2/Queue as Workers.

Project structure with Pages Functions

my-app/
├── src/                  # frontend code (Next.js, Astro, ...)
├── functions/            # Pages Functions
│   ├── api/
│   │   ├── posts.ts      # → /api/posts
│   │   └── contact.ts    # → /api/contact
│   └── _middleware.ts    # runs before every request
└── package.json

Pages Functions route automatically by filename — no extra config needed.

What Pages is good for

Full-stack app with a framework

Pages supports Next.js, Astro, SvelteKit, Remix, Nuxt, and more via adapters. Build once, Pages handles deploy and CDN:

# Next.js on Pages
npm install @cloudflare/next-on-pages
# deploy
wrangler pages deploy .vercel/output/static

Static site / blog

Blogs built with Astro, Hugo, Eleventy — Pages serves files fast from the edge, no server needed:

# Astro: build and deploy
npm run build
wrangler pages deploy dist

App with frontend + lightweight server

A SPA that needs a few endpoints — write Pages Functions instead of standing up a separate Worker:

// functions/api/products.ts
export async function onRequestGet(context: EventContext<Env, string, unknown>) {
  const products = await context.env.DB.prepare("SELECT * FROM products").all();
  return Response.json(products.results);
}

Branch preview deployments

Every PR automatically gets its own preview URL — useful for review before merging:

https://feat-new-checkout.my-app.pages.dev

Workers don't have this feature.


Pages Functions are Workers

A common misconception: Pages Functions are "less capable" than Workers. In reality, Pages Functions run on the same runtime as Workers — V8 isolates, edge network, same ability to bind KV/D1/R2/Queue.

The differences:

  • Pages Functions route by filesystem (functions/api/users.ts/api/users)
  • Workers routing is handled entirely in your code
  • Pages Functions don't support cron triggers
  • Pages Functions can't receive Queue messages directly

If your app is Next.js on Pages, all API routes (app/api/) run on the Workers runtime — you can still bind D1, KV, and R2 via process.env or context.env.


When to use Workers, when to use Pages

Use Workers when

  • Building a pure API backend with no frontend
  • Need cron triggers to run on a schedule
  • Need to receive and process Queue messages
  • Writing a proxy or middleware that intercepts requests before origin
  • Want full control over routing and logic without Pages' file-based structure
  • Small edge scripts: image resizing, auth checks, geo-based redirects

Use Pages when

  • Deploying a frontend framework: Next.js, Astro, SvelteKit, Remix
  • Need static hosting + CDN for HTML/CSS/JS/images
  • Want preview deployments per branch or PR
  • The API is simple enough to live in functions/ alongside the frontend
  • Prefer deploying by git push over maintaining a separate CI/CD pipeline

Use both when

This is the most common pattern for real-world apps:

Pages (Next.js frontend)
  └── bind KV, D1, R2

Workers (separate backend API)
  └── bind same KV, D1, R2
  └── cron triggers
  └── Queue consumer

The frontend calls the Worker API via fetch("/api/...") or a separate domain. Both bind to the same KV namespace and D1 database.


Free tier compared

Workers free tier

  • 100,000 requests/day — beyond this, $0.50 per million requests
  • 10ms CPU time per request on the free plan (CPU time, not wall time)
  • Cron triggers: 1 cron per Worker on the free plan
  • Unlimited number of Workers

Pages free tier

  • Unlimited requests — serving static files is completely free
  • 500 builds/month (enough for a small team)
  • 1 project on the free plan (unlimited on paid)
  • Preview deployments: unlimited
  • Pages Functions share the same quota as Workers — still counts toward the 100,000 requests/day limit

If your app is a pure static site with no Functions, Pages free tier is genuinely unlimited. The moment you add Functions, those requests count against the Workers limit.


Which one to choose

What you're building Use
API backend with no frontend Workers
Background jobs, cron, Queue consumer Workers
Proxy / edge middleware Workers
Blog, docs site, landing page Pages
Next.js / Astro / SvelteKit app Pages
Full-stack with frontend + lightweight API Pages (+ Functions)
Complex frontend + heavy backend Pages + separate Workers

If you're still unsure, a simple rule: if you have a frontend, use Pages; if you don't, use Workers. Pages Functions are capable enough for most lightweight backend needs, and moving a Function to a standalone Worker later is straightforward if the project grows.