Migrating a Cloudflare SvelteKit Project from Pages to Workers

Cole
by Cole Crouter
Posted on 2025-09-20, 11:03 p.m.
Tags: sveltesveltekitcloudflareworkers

Migrating a Cloudflare SvelteKit Project from Pages to Workers

Phew, that’s a bit of a mouthful.

If you’re just looking for the guide, skip to the migration section.


Back in April, Cloudflare announced that Pages is being sunset in favour of Workers. This may come as a surprise to many, as it felt like just yesterday (it was actually in 2020, you old thing) Cloudflare Pages was being announced.

If you’re interested in the history behind these two products, and why Cloudflare is pulling their product, you won’t find much outside of guides on how to migrate. Conversely, if you search for how to migrate SvelteKit, you will find… *checks notes* …approximately no useful info.

If you check out the Cloudflare Docs about framework-specific migration, you’ll see the following:

Switch out any Pages-specific adapters for the Workers equivalent and follow any guidance that they (the framework authors) provide.

If you then make the smart move and check out the SvelteKit docs, all you will find is a confusing “Migrating from Workers Sites” section which is actually not related (“Workers Sites” is the spiritual predecessor of Pages, before Pages launched in 2021). The only actual reference I could find was under the Cloudflare Workers section, which unhelpfully reminds you of the following:

adapter-cloudflare-workers has been deprecated in favour of adapter-cloudflare. We recommend using adapter-cloudflare to deploy to Cloudflare Workers with Static Assets since Cloudflare Workers Sites will be deprecated in favour of it.

That being said, let me walk you through a brief history, some reasons why you might want to migrate, and my experience migrating one of my projects.

A History of Innovation (and Deprecation)

Cloudlare Pages launched in early 2021 as an alternative to Workers Sites. The idea was simple; provide a service that allows users to easily host & build static sites for free. It works like so:

  1. GitHub commit hook
  2. Cloudflare spins up Docker image with Go, Python, JS, etc.
  3. Build command runs
  4. Output is uploaded to Cloudflare KV
  5. Assets served by a hidden Worker

You could manually achieve something similar with Workers, by uploading your assets to Cloudflare KV (Workers Sites, essentially). The advantage is that you wouldn’t pay for the KV reads. Aside from that and the “revolutionary” CI pipeline, Pages also came with a host of QoL features, including deployments (for rollbacks & preview builds).

Since Pages was built on Workers, it also had allowed you to provide your own additional worker code. This is how modern full-stack JS frameworks on Cloudflare Pages works today!

A Worthwile (but Frustrating) Struggle

At a technological level, Pages & Workers grew apart over time. The Pages team focused on improving build speeds & their Docker image, as well as adding features to the dashboard. On the other hand, the Workers team focused on adding new bindings and platform integrations. Both platforms ended up playing catch-up with the other, with huge integrations (e.g. D1 beta, Durable Objects) not being added to Pages until much later (or even missing still).

End of a Painful Era

In recent years, and after internal restructures, Cloudflare announced a new initiative to refresh their dashboard. It wasn’t long until the dashboards for Workers & Pages started to consolidate, and were eventually merged. This leaves us at today, where development on Pages has long-since ceased, and Workers now shares all of Pages’ functionality.

Why Switch?

This level of deprecation is quite tame when compared to something Google cooks up on a monthly basis, but Cloudflare never actually states what you stand to gain by switching, aside from a vague “going forward, we recommend…”, so what’s the big deal?

Features

As stated above, there are bindings & configurations that exist only for Workers (long-time Wrangler enthusiasts are well aware of this). Features like rate limiters, which have legitimate uses (aside from embracing vendor lock-in) remain Workers-only.

Workers’ dashboards are now also more featured than Pages’, including metrics for traffic, errors, etc. They also have persistent logs which is huge (sorry Wrangler bros 😭).

Limitations

Historically, Workers & Pages have shared the same resource limits. 128MB of memory, 1GB (now 2?) code/asset size-limit, etc. Even under the “Limits” section of Pages’ documentation, they say:

Requests to Pages functions count towards your quota for Workers plans, including requests from your Function to KV or Durable Object bindings.

Pages supports the Standard usage model. (link to Workers’ limits page)

However, this is actually not true…

I recently was troubleshooting an issue on my up-and-coming hit website, Steam Vault, where I consistently hit memory/CPU time limits. I remember Pages CPU limit used to be 100ms or something in that magnitude. While I was looking for the quintessential “I am a bad dev & need higher limits”-form, I noticed that the Workers CPU limit (as pointed to directly by Pages’ docs) had its default lifted to 30 seconds.

In the end, I migrated the project to Workers. Sure enough, it hasn’t crashed since. It’s still really slow though, unfortunately… I might need that link after all 😖

It’s safe to say that small tweaks from Workers won’t trickle dont to Pages anymore, and likely haven’t been for the past 1-2 years.

OK, Let’s Migrate this Bad Boy 😎

As mentioned above, there is a severe lack of instructions anywhere for migrating a SvelteKit project. Let this be your guide. We will follow this outline:

  • Update your wrangler.{toml|json|jsonc}
  • Manually deploy to Workers
  • Copy over build settings/secrets
  • Delete Pages project
  • Set up Git integration
  • Reassign your domain

Update Wrangler Config

This one’s easy. As per the Cloudflare migration docs, I did the following change to my wrangler.toml file.

- pages_build_output_dir = ".svelte-kit/cloudflare"
+ main = ".svelte-kit/cloudflare/_worker.js"
+ assets.directory = ".svelte-kit/cloudflare"
+ assets.binding = "STATIC_ASSETS"

If you don’t have a wrangler.toml file (or aren’t sure what that is), you can generate one with npx wrangler pages download config <PROJECT_NAME>.

Ship, Ship, Ship

Build your project, then run npx wrangler deploy (NOT TO BE CONFUSED WITH npx wrangler pages deploy!!!). If you reload the Cloudflare Dashboard, you should be able to see your new Worker entry.

Registrar? I Barely Know Har

From the same dashboard, remove your domain record from the Pages project (under Custom Domains) and add it to the new Worker project (under Settings -> Domains & Routes).

Real Developers Don’t Have Secrets

In the dashboard, take the time to copy over your build output settings from the Pages. For some reason, all of my secrets seemed to copy over on their own. This eludes me, because Workers & Pages don’t share secret stores (another ✊😫 for true Wrangler enthusiasts). Ah well, if I ever lose them, I always have my good ol’ monitor sticky note.

Ctrl+C [Project Name] And Ctrl+V Into Confirmation Box

This is a fun one. Click on your Pages project, Settings -> General -> big red Delete button.

If you have over 100 deployments (I too am lazy), you will most likely get a weird error saying “your project has too many deployments to delete.” What? I want to speak to your manager!

Cloudflare helpfully offers a guide to solving this issue, which involves downloading & extracting a ZIP file, npm install-ing it, generating an API key with the right scope, remembering how to set env variables on Windows, then finally running the script. Why couldn’t it just be a button the the dashboard? I really don’t know.

However, if you’re a stupid keyboard-goblin who’s fragile ego imcomparable skill won’t let you npm install on a glorified auto-clicker, I have the perfect solution for you!

document.querySelector('#all_deployments > div > table > tbody > tr:last-child > td:nth-child(6) > div > button').click()
    || document.querySelector('#all_deployments > div > table > tbody > tr:last-child > td > div > div > button:last-child').click()
    || document.querySelector('#focusFallback > div > form > div > div > div > button:nth-child(2)').click()

—or in it’s most useful form:

setInterval(() =>
    document.querySelector('#all_deployments > div > table > tbody > tr:nth-child(2) > td:nth-child(6) > div > button').click()
        || document.querySelector('#all_deployments > div > table > tbody > tr:nth-child(2) > td > div > div > button:nth-child(3)').click()
        || document.querySelector('#focusFallback > div > form > div > div > div > button:nth-child(2)').click(),
2000);

Simply open the Pages project’s “Deployments” tab, open Dev Tools, run this sucker. If you have any preview deployments, I hope you can type fast 😉. Cloudflare, if you’re reading this, my salary expectations are negotiable.

We’re Git-ing to the End

The final step is to set up Git integration on the Worker project by opening it’s dashboard and clicking the little Git button in the top right.

Closing Thoughts

If you’re trying to migrate your project, I hope you made it this far. Otherwise, I hope you learned something. I might open an issue/PR on the Svelte repo if I think of an appropriate way to improve the existing instructions.