Web Development

Why I Moved from Pages to Workers & What I Learned

I migrated my site from Cloudflare Pages to Workers. Discover the limitations I hit, the key lessons I learned, and why this powerful shift was a game-changer.

D

Daniel Carter

Senior full-stack developer specializing in edge computing and modern web architectures.

7 min read20 views

There’s a certain beauty to a static site. It’s fast, secure, and wonderfully simple. For a long time, my personal blog, running on Cloudflare Pages, was my pride and joy. The git-push-to-deploy workflow was a dream, and the performance was incredible. It was, for all intents and purposes, the perfect setup.

Until it wasn’t.

The first cracks appeared not as a sudden break, but as a slow trickle of “what ifs.” What if I could generate dynamic social media images for each post? What if I could run A/B tests with more complex logic than a simple redirect? What if I needed to stitch data from multiple APIs before serving a page? My perfect static setup started to feel… constrained. This is the story of my migration from the comfortable simplicity of Cloudflare Pages to the boundless power of Cloudflare Workers, and the crucial lessons I learned along the way.

The Tipping Point: When Static Isn't Enough

Cloudflare Pages is phenomenal for its intended purpose: serving static assets with incredible speed and zero fuss. The integrated Functions give you a taste of serverless, but they operate as distinct endpoints. My needs started to blur the lines between the static content and the dynamic logic.

Here were my three main drivers for the change:

  1. Truly Dynamic OG Images: I was tired of the generic social media cards. I wanted each blog post to have a unique, beautifully generated Open Graph image with the title overlaid. Doing this with Pages Functions was possible, but clunky. I wanted the image generation to happen at the same edge location as the site itself, on a custom path, without a separate function invocation.
  2. Sophisticated Middleware Logic: I wanted to implement advanced A/B testing. For example, show a new layout to 5% of users from Canada using a specific browser, while also logging the event. This kind of request-intercepting logic is the bread and butter of a service worker model, and it felt unnatural to shoehorn into Pages' file-based routing.
  3. API Orchestration at the Edge: A new feature I envisioned required fetching data from a headless CMS, user data from a separate auth service, and product info from a third-party API. I wanted to combine all this at the edge, create a single, clean JSON object, and then use it to server-side render a portion of the page. A Worker is explicitly designed for this—to be a programmable layer between the user and my services.

Pages was telling me, “Here are your files.” I needed something that said, “What do you want to do with this request?”

The Migration Playbook: From Pages to Workers

The migration felt daunting at first, but breaking it down made it manageable. The goal wasn't just to replicate my Pages site; it was to build a better, more powerful foundation.

Step 1: Setup with Wrangler

The first step was embracing the command line. Cloudflare's CLI, Wrangler, is the control center for Workers development.

# Install the latest version of Wrangler
npm install -g wrangler

# Create a new Worker project
wrangler init my-edge-app

# This scaffolds a new project with a simple "Hello World" worker
cd my-edge-app

This simple process immediately shifted my mindset from a Git-based workflow to a more traditional (yet modern) software development lifecycle with local testing and explicit deployments.

Advertisement

Step 2: Handling Static Assets

Where do the HTML, CSS, and JS files go? I had two main options:

  1. Bundle them with the Worker: For a small site, you can import assets directly into your Worker script and serve them from memory. This is fast but can bloat your Worker's size.
  2. Use Cloudflare R2: This was my choice. R2 is S3-compatible object storage with zero egress fees. I uploaded my entire static `_site` or `build` directory to an R2 bucket.

My Worker's code then became a smart router. If a request was for a static asset (e.g., `/css/style.css`), it would fetch it from the R2 bucket and serve it. Otherwise, it would handle the request dynamically.

Step 3: Recreating Routing and Redirects

My `_redirects` file from Pages had to be converted into code. This was surprisingly liberating. Instead of a static list of rules, I now had the full power of JavaScript.

// A simplified example in my worker's fetch handler

const { pathname } = new URL(request.url);

// Simple redirect logic
const redirects = {
  '/old-post': '/new-post',
  '/about-us': '/about'
};

if (redirects[pathname]) {
  return Response.redirect(new URL(redirects[pathname], request.url), 301);
}

// Handle other routes...

This code-based approach allowed me to add logging, analytics, or feature flags to my redirects—something I could never do with a simple config file.

Head-to-Head: A Pragmatic Comparison

Choosing between Pages and Workers isn't about which is “better,” but which is right for your specific needs. Here’s how I see them stack up now.

Feature Cloudflare Pages Cloudflare Workers
Primary Use Case Static site hosting (Jamstack) Dynamic applications & APIs at the edge
Deployment Model Git-based (push to deploy) CLI-based (`wrangler deploy`)
Dynamic Capabilities Via separate Functions endpoints Full control over request/response cycle in one script
Developer Experience Extremely simple, zero-config for many frameworks Requires coding, local testing, and managing a deployment process
Flexibility Limited to file-based routing and config files Nearly limitless; logic is defined by your code
Ecosystem Integration Good (integrates with KV, R2 via Functions) Native (designed to be the glue for R2, KV, D1, Queues, etc.)

Four Key Lessons from the Trenches

The migration was more than a technical exercise. It fundamentally changed how I approach building for the web.

“Moving to Workers wasn't about abandoning the static-first philosophy; it was about enhancing it with a powerful, programmable edge.”

Lesson 1: Code is the Ultimate Configuration

The biggest mental shift was moving from configuration files (`_redirects`, `_headers`) to JavaScript. While config files are easy, they are inherently limiting. With a Worker, your routing, headers, and security policies are all just code. This means you can version control them, write tests for them, and make them as dynamic as you need.

Lesson 2: The Learning Curve is Real (But Worth It)

Don't underestimate the initial learning curve. You need to get comfortable with Wrangler, the `fetch` API-based handler, and the nuances of the edge runtime (e.g., no Node.js APIs like `fs`). However, once you clear that initial hurdle, the speed at which you can build powerful, globally distributed applications is astounding.

Lesson 3: Rethinking “Static” Serving

I learned that a Worker can be an incredibly efficient static asset server. By pairing it with R2, my Worker now acts as a gatekeeper. It serves static files instantly, but it can also intercept any request to inject dynamic content, perform authentication, or personalize the experience before the user ever sees the page. My site is still “static” in spirit, but with dynamic superpowers.

Lesson 4: Unlocking the Full Edge Ecosystem

Pages Functions can talk to other Cloudflare services, but Workers feel like the native language of the ecosystem. Within a single request, my Worker can read from KV (for configuration), write to R2 (for uploads), query D1 (a serverless SQL database), and send a message to a Queue for background processing. It’s the central hub that makes all these powerful services work together seamlessly.

Was It Worth It? My Final Verdict

Absolutely. While I’ll always have a soft spot for the elegant simplicity of Cloudflare Pages, my needs outgrew its design.

Cloudflare Pages is the perfect choice if you have a static or Jamstack site and your dynamic needs can be neatly contained within separate API functions. Its git-driven workflow is second to none for that use case.

It’s time to move to Cloudflare Workers when your application logic needs to be an integral part of the request-response cycle itself. If you find yourself wishing you could just write an `if` statement to control a redirect, or if the boundary between your “static” site and your “dynamic” functions is becoming blurry, it’s time to make the leap.

The move to Workers was an investment in flexibility. It gave me a platform where the only limitation is what I can code, and in today's fast-moving web landscape, that freedom is the most valuable asset of all.

Tags

You May Also Like