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; use adapter-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

TierLimit
Free3MB compressed per script
Paid10MB 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

TierLimit
Free10ms CPU time per request
Paid30s 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, not app/entry.server.tsx (different from Remix)
  • If you’re migrating from Remix, the Cloudflare template structure is different
  • context.cloudflare.env is the pattern, not context.env or process.env

Astro

  • Adapter v13 breaking change: Astro.locals.runtime is gone. Use import { env } from "cloudflare:workers" instead
  • CJS dependencies may break: The workerd dev server doesn’t handle CJS the same as Node.js. Add problematic deps to vite.ssr.external
  • platformProxy config removed: No longer needed since dev uses real workerd

SvelteKit

  • platform is undefined in dev (npm run dev). Always use optional chaining: platform?.env?.MY_KV
  • adapter-cloudflare-workers is deprecated. Use adapter-cloudflare (no -workers)
  • Test with wrangler pages dev .svelte-kit/cloudflare before deploying to catch workerd issues

Next.js (OpenNext)

  • Build with @opennextjs/cloudflare build, not next build
  • next dev runs Node.js. You won’t catch edge issues until wrangler dev
  • ISR requires a KV namespace bound as NEXT_CACHE_WORKERS_KV
  • Default next/image loader 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 dev is 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 types every 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 RuntimeFrameworks
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:

  1. Always test with wrangler dev or wrangler pages dev before deploying
  2. Use wrangler types to keep binding types accurate
  3. Be suspicious of any code using Node.js-specific APIs
  4. 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_compat to 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() from wrangler to get local binding emulation
  • Use optional chaining and provide fallback values in dev
  • Switch to a CF Vite Plugin framework for full parity