skip to content

#cloudflare

#tech

#workers

Migrating an 11ty site from Cloudflare Pages to Workers

It’s Workers all the way down.


If you’re reading this, I’m guessing you’ve got an 11ty site live on Cloudflare Pages somewhere, and you’ve probably come across one of the many Cloudflare messages and prompts asking you nicely to move off Pages and over to Workers. It’s a bummer, because Cloudflare Pages is a really great product!

If you’ve used GitHub Pages or Netlify, Cloudflare Pages is easy to pick up. But diving into the Worker docs (and even the Migration docs), they can seem pretty complicated, because, well... they are. For now, let’s try and skip past all the complex stuff and just get a Worker to serve our 11ty build.

I’m gonna start with some assumptions:

Let’s get into it!

1. Install wrangler and login

wrangler is the tool for managing your Worker (both for local development, and deploying to production). The docs recommend installing wrangler as a project dependency via npm i -D wrangler@latest. That’s generally good advice, but if you prefer managing things via brew, or want to install it globally, you can absolutely just do that.

Once installed, run npx wrangler login, which will open a browser window asking you to sign in to Cloudflare and authorise wrangler.

2. Configure your Worker

In your project root (probably where your 11ty config lives), add a new wrangler.jsonc file (wrangler.toml is also valid, but JSONC seems like the recommended path these days).

You might already have this file if you deployed your Pages project via code. If so, make sure to remove pages_build_output_dir.

Below is an example for my site, you can fill in the blanks.

{
  "name": "website-benwhite",
  "compatibility_date": "2025-05-05",
  "assets": {
    "directory": "_site",
    "not_found_handling": "404-page",
  },
  "routes": [{ "pattern": "benwhite.com.au", "custom_domain": true }],
}

The assets config is basically the most important bit, and tells the Worker where to find all the files for your site build. The Static Assets docs have a bunch more detail.

Defining the route in code is just a neat way to reduce the clicks needed in connecting your DNS to the Worker. But as mentioned above, if you want to handle the routes via the UI instead, you can just remove that line.

By not defining a main script file AND setting up assets, we’re letting Cloudflare know we want to create an “assets-only Worker”. If you want to change this later to move over your Cloudflare Pages Functions, that’s totally fine. All of these choices can be changed later without fuss.

3. Update .gitignore and package.json.

Add the .wrangler/ directory to .gitignore.

Now, it’s time to connect some dots with package.json scripts. Here’s how I’ve done it:

"scripts": {
  "11ty:build": "npx @11ty/eleventy",
  "11ty:watch": "npx @11ty/eleventy --watch --quiet --ignore-initial",
  "11ty:benchmark": "DEBUG=Eleventy:Benchmark* npm run build",
  "build": "11ty:build",
  "util:rimraf": "npx rimraf _site",
  "util:killport": "npx kill-port --port 8787",
  "start": "npm-run-all util:rimraf util:killport 11ty:build --parallel 11ty:watch wrangler:dev",
  "wrangler:dev": "npx wrangler dev --live-reload",
  "wrangler:deploy": "npx wrangler deploy"
},

No two package.json files are alike, so I’ll just step through the important stuff:

How you handle this is very much up to how you handle your own build, but they key takeaway is that your 11ty --watch process should run in parallel with wrangler dev. This will allow live reload to work.

4. Test and deploy

Run npm run start (or however you kick off your local stuff), and see if everything works as expected. You’ll note wrangler spins up your Worker on 8787. You can change that (and many other settings) if you want.

If everything is all okay, run the deploy script npm run wrangler:deploy (or just run it directly with npx wrangler deploy).

NOTE: If you set up your domain as a route in the wrangler.jsonc but Cloudflare DNS already has a record assigned for that domain/subdomain, you will likely get an error here. You’ll need to manually delete it via the Cloudflare DNS web UI before you’re allowed to deploy. Keep a screenshot of those settings before removing, just in case you want to roll back.

And you’re done!

I’ll dive into moving functions over soon. Until then, I’ve shared some example wrangler config snippets that should hopefully give you a head start.

If you’re getting stuck with your move, I’m happy to chat and help where I can, just ping me on Mastodon.