Gotchas
Cross-framework pitfalls when deploying to Cloudflare Workers. These are the things that will waste your afternoon if you don’t know about them upfront.
Platform Gotchas
Pages is Dead, Long Live Workers
Cloudflare is migrating from Pages to Workers with Static Assets. New projects should target Workers.
- Astro adapter v13 dropped Pages support entirely
adapter-cloudflare-workers(SvelteKit) is deprecated; useadapter-cloudflare- Cloudflare’s new framework guides all target Workers, not Pages
- Existing Pages projects keep working, but new features ship for Workers only
workerd is Not Node.js
The most common source of bugs. These Node.js APIs don’t exist in workerd:
// These will fail at runtime:
import fs from "fs"; // No filesystem
import path from "path"; // No path module (without nodejs_compat)
import { exec } from "child_process"; // No child processes
process.env.MY_VAR; // No process.env (use env bindings)
__dirname; // Doesn't exist in ESM workers
require("something"); // No require (ESM only)
Fix: Enable nodejs_compat in wrangler.jsonc for polyfills. But filesystem and process APIs remain unavailable.
Script Size Limits
| Tier | Limit |
|---|---|
| Free | 3MB compressed per script |
| Paid | 10MB compressed per script |
Next.js via OpenNext can easily exceed 3MB. Nuxt is borderline. Check your bundle:
npx wrangler deploy --dry-run
CPU Time Limits
| Tier | Limit |
|---|---|
| Free | 10ms CPU time per request |
| Paid | 30s CPU time per request (configurable up to 5min via limits.cpu_ms) |
Gotcha: This is CPU time, not wall-clock time. A request that
awaits a KV read for 50ms but only uses 2ms of CPU is fine on free tier. But heavy JSON parsing, crypto operations, or template rendering will hit the limit fast.
Framework-Specific Gotchas
React Router v7
- Entry point is
workers/app.ts, notapp/entry.server.tsx(different from Remix) - If you’re migrating from Remix, the Cloudflare template structure is different
context.cloudflare.envis the pattern, notcontext.envorprocess.env
Astro
- Adapter v13 breaking change:
Astro.locals.runtimeis gone. Useimport { env } from "cloudflare:workers"instead - CJS dependencies may break: The
workerddev server doesn’t handle CJS the same as Node.js. Add problematic deps tovite.ssr.external platformProxyconfig removed: No longer needed since dev uses realworkerd
SvelteKit
platformisundefinedin dev (npm run dev). Always use optional chaining:platform?.env?.MY_KVadapter-cloudflare-workersis deprecated. Useadapter-cloudflare(no-workers)- Test with
wrangler pages dev .svelte-kit/cloudflarebefore deploying to catchworkerdissues
Next.js (OpenNext)
- Build with
@opennextjs/cloudflare build, notnext build next devruns Node.js. You won’t catch edge issues untilwrangler dev- ISR requires a KV namespace bound as
NEXT_CACHE_WORKERS_KV - Default
next/imageloader doesn’t work on Workers. Use a custom loader or Cloudflare Images - OpenNext is community-maintained. There’s lag between Next.js releases and adapter updates
Hono
- JSX is server-side only. No hydration, no client-side routing, no React-style interactivity
wrangler devis sufficient for Hono. You don’t need Vite unless you’re bundling client assets
TanStack Start
- Newer framework, smaller community, fewer production examples on Cloudflare
- Server functions use
createServerFn, not loaders/actions - Documentation is still evolving
Binding Type Generation
All frameworks benefit from generated types. Run this after changing wrangler.jsonc:
npx wrangler types
This creates a worker-configuration.d.ts file with your Env type. Without it, you’re typing bindings manually and hoping they match your config.
Gotcha: Re-run
wrangler typesevery time you add, remove, or rename a binding. Stale types are worse than no types.
Dev/Prod Parity
The single biggest quality-of-life factor when choosing a framework:
| Dev Runtime | Frameworks |
|---|---|
workerd (full parity) | React Router v7, TanStack Start, Astro v13, Hono, Vite+React SPA |
| Node.js (parity gap) | SvelteKit, Nuxt, Qwik, SolidStart, Next.js (OpenNext) |
If your framework runs dev in Node.js:
- Always test with
wrangler devorwrangler pages devbefore deploying - Use
wrangler typesto keep binding types accurate - Be suspicious of any code using Node.js-specific APIs
- Consider adding a CI step that builds and runs in
workerd
Remix is Dead
If you find Remix tutorials, templates, or Stack Overflow answers for Cloudflare, they’re outdated. Cloudflare’s docs explicitly say Remix is “no longer recommended for new projects.” Use React Router v7 instead; it’s the official successor.
Common Deployment Issues
”Your Worker exceeded the size limit”
Your server bundle is too large. Options:
- Audit dependencies. Tree-shake unused code
- Move to a lighter framework (Hono for APIs, Astro for content)
- Upgrade to paid Workers (10MB limit)
“Could not resolve module”
A dependency uses Node.js APIs not available in workerd. Options:
- Add
nodejs_compatto compatibility flags - Find a Workers-compatible alternative
- Add the dependency to
vite.ssr.external(for build-time resolution)
“Bindings are undefined in dev”
You’re using a framework without the CF Vite Plugin and platform/context isn’t populated during dev. Options:
- Use
getPlatformProxy()fromwranglerto get local binding emulation - Use optional chaining and provide fallback values in dev
- Switch to a CF Vite Plugin framework for full parity