DEV Community

中川倖成
中川倖成

Posted on

Why Next.js 15 randomly fails on Cloudflare Workers (and how to fix it)

If you've tried deploying Next.js 15 App Router to
Cloudflare Workers, you've probably hit builds that
randomly fail with cryptic errors.

I've been running a production app on CF Workers for
months. Here are the 4 bugs I found — none of them
are documented in one place.

Bug 1: Race condition on pages-manifest.json

Next.js 15.5+ uses worker threads during build. This
causes intermittent ENOENT errors:

Error: ENOENT: no such file or directory,
open '.next/server/pages-manifest.json'
Enter fullscreen mode Exit fullscreen mode

Fix: Disable worker threads in your build script:

process.env.NEXT_PRIVATE_WORKER_THREADS = 'false'
Enter fullscreen mode Exit fullscreen mode

Bug 2: pages-manifest.json is never generated

App Router-only projects don't generate
pages-manifest.json — but opennextjs-cloudflare
requires it at deploy time.

Fix: Pre-create it after build:

const manifest = {
  '/_app': 'pages/_app.js',
  '/_document': 'pages/_document.js',
  '/_error': 'pages/_error.js',
}
fs.writeFileSync(manifestPath, JSON.stringify(manifest))
Enter fullscreen mode Exit fullscreen mode

Bug 3: Turbopack stubs break esbuild

Next.js generates Pages Router stubs that reference
[turbopack]_runtime.js. This causes esbuild to
fail silently during the Cloudflare bundle step.

Fix: Delete the stubs after Next.js build completes:

const stubFiles = ['_app.js', '_document.js', '_error.js']
for (const f of stubFiles) {
  const p = path.join(pagesDir, f)
  if (fs.existsSync(p)) fs.unlinkSync(p)
}
Enter fullscreen mode Exit fullscreen mode

Bug 4: Supabase auth cookies break on edge

The standard Supabase client stores cookies in a way
that doesn't work on Cloudflare Workers (no
document.cookie access in SSR context).

Fix: Use @supabase/ssr with explicit cookie handlers:

import { createServerClient } from '@supabase/ssr'

export function createClient() {
  const cookieStore = cookies()
  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() { return cookieStore.getAll() },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value, options }) =>
            cookieStore.set(name, value, options)
          )
        },
      },
    }
  )
}
Enter fullscreen mode Exit fullscreen mode

Putting it all together

All 4 fixes live in a single scripts/build.mjs that
wraps next build. Run it instead of next build
directly.

I packaged this build script along with all the other
patterns (Tailwind CSS 4, i18n, PWA, security headers,
WCAG 2.2 AA) into a starter template:

👉 Edge Starter on Lemon Squeezy — $39

The template builds consistently and is tested in
production. Happy to answer questions about any of
the fixes in the comments.


Enter fullscreen mode Exit fullscreen mode

Top comments (0)