
Cloudflare Nextjs
- 113 installs
- 16 repo stars
- Updated November 20, 2025
- jackspace/claudeskillz
Deploy and operate Next.js apps on Cloudflare Workers/Pages with correct routing, env config, edge caching, and production-ready build settings.
About
Covers deploying Next.js applications to Cloudflare using Workers, Pages, and OpenNext-style adapters. It addresses wrangler configuration, edge runtime limits, caching, environment variables, and domain setup so SaaS and content sites launch globally with fast cold starts and maintainable CI deploy pipelines.
- Next.js on Cloudflare Workers/Pages
- Wrangler and environment configuration
- Edge routing and caching strategy
- Build adapter and runtime constraints
- Production domain and preview workflows
Cloudflare Nextjs by the numbers
- 113 all-time installs (skills.sh)
- +1 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #546 of 1,039 Cloud & Infrastructure skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/jackspace/claudeskillz --skill cloudflare-nextjsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 113 |
|---|---|
| repo stars | ★ 16 |
| Last updated | November 20, 2025 |
| Repository | jackspace/claudeskillz ↗ |
What it does
Deploy and operate Next.js apps on Cloudflare Workers/Pages with correct routing, env config, edge caching, and production-ready build settings.
Files
Cloudflare Next.js Deployment Skill
Deploy Next.js applications to Cloudflare Workers using the OpenNext Cloudflare adapter for production-ready serverless Next.js hosting.
Use This Skill When
- Deploying Next.js applications (App Router or Pages Router) to Cloudflare Workers
- Need server-side rendering (SSR), static site generation (SSG), or incremental static regeneration (ISR) on Cloudflare
- Migrating existing Next.js apps from Vercel, AWS, or other platforms to Cloudflare
- Building full-stack Next.js applications with Cloudflare services (D1, R2, KV, Workers AI)
- Need React Server Components, Server Actions, or Next.js middleware on Workers
- Want global edge deployment with Cloudflare's network
Key Concepts
OpenNext Adapter Architecture
The OpenNext Cloudflare adapter (@opennextjs/cloudflare) transforms Next.js build output into Cloudflare Worker-compatible format. This is fundamentally different from standard Next.js deployments:
- Node.js Runtime Required: Uses Node.js runtime in Workers (NOT Edge runtime)
- Dual Development Workflow: Test in both Next.js dev server AND workerd runtime
- Custom Build Pipeline:
next build→ OpenNext transformation → Worker deployment - Cloudflare-Specific Configuration: Requires wrangler.jsonc and open-next.config.ts
Critical Differences from Standard Next.js
| Aspect | Standard Next.js | Cloudflare Workers |
|---|---|---|
| Runtime | Node.js or Edge | Node.js (via nodejs_compat) |
| Dev Server | next dev | next dev + opennextjs-cloudflare preview |
| Deployment | Platform-specific | opennextjs-cloudflare deploy |
| Worker Size | No limit | 3 MiB (free) / 10 MiB (paid) |
| Database Connections | Global clients OK | Must be request-scoped |
| Image Optimization | Built-in | Via Cloudflare Images |
| Caching | Next.js cache | OpenNext config + Workers cache |
Setup Patterns
New Project Setup
Use Cloudflare's create-cloudflare (C3) CLI to scaffold a new Next.js project pre-configured for Workers:
npm create cloudflare@latest -- my-next-app --framework=nextWhat this does: 1. Runs Next.js official setup tool (create-next-app) 2. Installs @opennextjs/cloudflare adapter 3. Creates wrangler.jsonc with correct configuration 4. Creates open-next.config.ts for caching configuration 5. Adds deployment scripts to package.json 6. Optionally deploys immediately to Cloudflare
Development workflow:
npm run dev # Next.js dev server (fast reloads)
npm run preview # Test in workerd runtime (production-like)
npm run deploy # Build and deploy to CloudflareExisting Project Migration
To add the OpenNext adapter to an existing Next.js application:
1. Install the adapter
npm install --save-dev @opennextjs/cloudflare2. Create wrangler.jsonc
{
"name": "my-next-app",
"compatibility_date": "2025-05-05",
"compatibility_flags": ["nodejs_compat"]
}Critical configuration:
compatibility_date: Minimum `2025-05-05` (for FinalizationRegistry support)compatibility_flags: Must include `nodejs_compat` (for Node.js runtime)
3. Create open-next.config.ts
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
export default defineCloudflareConfig({
// Caching configuration (optional)
// See: https://opennext.js.org/cloudflare/caching
});4. Update package.json scripts
{
"scripts": {
"dev": "next dev",
"build": "next build",
"preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview",
"deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy",
"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts"
}
}Script purposes:
dev: Next.js development server (fast iteration)preview: Build + run in workerd runtime (test before deploy)deploy: Build + deploy to Cloudflarecf-typegen: Generate TypeScript types for Cloudflare bindings
5. Ensure Node.js runtime (not Edge)
Remove Edge runtime exports from your app:
// ❌ REMOVE THIS (Edge runtime not supported)
export const runtime = "edge";
// ✅ Use Node.js runtime (default)
// No export needed - Node.js is defaultDevelopment Workflow
Dual Testing Strategy
Always test in BOTH environments:
1. Next.js Dev Server (npm run dev)
- Fast hot reloading
- Best developer experience
- Runs in Node.js (not production runtime)
- Use for rapid iteration
2. Workerd Runtime (npm run preview)
- Runs in production-like environment
- Catches runtime-specific issues
- Slower rebuild times
- Required before deployment
When to Use Each
# Iterating on UI/logic → Use Next.js dev server
npm run dev
# Testing integrations (D1, R2, KV) → Use preview
npm run preview
# Before deploying → ALWAYS test preview
npm run preview
# Deploy to production
npm run deployConfiguration Requirements
Wrangler Configuration
Minimum requirements in wrangler.jsonc:
{
"name": "your-app-name",
"compatibility_date": "2025-05-05", // Minimum for FinalizationRegistry
"compatibility_flags": ["nodejs_compat"] // Required for Node.js runtime
}Environment Variables for Package Exports
If using npm packages with multiple export conditions, create .env:
WRANGLER_BUILD_CONDITIONS=""
WRANGLER_BUILD_PLATFORM="node"This ensures Wrangler prioritizes the node export when available.
Cloudflare Bindings Integration
Add bindings in wrangler.jsonc:
{
"name": "your-app-name",
"compatibility_date": "2025-05-05",
"compatibility_flags": ["nodejs_compat"],
// D1 Database
"d1_databases": [
{
"binding": "DB",
"database_name": "production-db",
"database_id": "your-database-id"
}
],
// R2 Storage
"r2_buckets": [
{
"binding": "BUCKET",
"bucket_name": "your-bucket"
}
],
// KV Storage
"kv_namespaces": [
{
"binding": "KV",
"id": "your-kv-id"
}
],
// Workers AI
"ai": {
"binding": "AI"
}
}Access bindings in Next.js via process.env:
// app/api/route.ts
import type { NextRequest } from 'next/server';
export async function GET(request: NextRequest) {
// Access Cloudflare bindings
const env = process.env as any;
// D1 Database query
const result = await env.DB.prepare('SELECT * FROM users').all();
// R2 Storage access
const file = await env.BUCKET.get('file.txt');
// KV Storage access
const value = await env.KV.get('key');
// Workers AI inference
const response = await env.AI.run('@cf/meta/llama-3-8b-instruct', {
prompt: 'Hello AI'
});
return Response.json({ result });
}Error Prevention (10+ Documented Errors)
1. Worker Size Limit Exceeded (3 MiB - Free Plan)
Error: "Your Worker exceeded the size limit of 3 MiB"
Cause: Workers Free plan limits Worker size to 3 MiB (gzip-compressed)
Solutions:
- Upgrade to Workers Paid plan (10 MiB limit)
- Analyze bundle size and remove unused dependencies
- Use dynamic imports to code-split large dependencies
Bundle analysis:
npx opennextjs-cloudflare build
cd .open-next/server-functions/default
# Analyze handler.mjs.meta.json with ESBuild Bundle AnalyzerSource: https://opennext.js.org/cloudflare/troubleshooting#worker-size-limits
---
2. Worker Size Limit Exceeded (10 MiB - Paid Plan)
Error: "Your Worker exceeded the size limit of 10 MiB"
Cause: Unnecessary code bundled into Worker
Debug workflow: 1. Run npx opennextjs-cloudflare build 2. Navigate to .open-next/server-functions/default 3. Analyze handler.mjs.meta.json using ESBuild Bundle Analyzer 4. Identify and remove/externalize large dependencies
Source: https://opennext.js.org/cloudflare/troubleshooting#worker-size-limits
---
3. FinalizationRegistry Not Defined
Error: "ReferenceError: FinalizationRegistry is not defined"
Cause: compatibility_date in wrangler.jsonc is too old
Solution: Update compatibility_date to 2025-05-05 or later:
{
"compatibility_date": "2025-05-05" // Minimum for FinalizationRegistry
}Source: https://opennext.js.org/cloudflare/troubleshooting#finalizationregistry-is-not-defined
---
4. Cannot Perform I/O on Behalf of Different Request
Error: "Cannot perform I/O on behalf of a different request"
Cause: Database client created globally and reused across requests
Problem code:
// ❌ WRONG: Global DB client
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
export async function GET() {
// This will fail - pool created in different request context
const result = await pool.query('SELECT * FROM users');
return Response.json(result);
}Solution: Create database clients inside request handlers:
// ✅ CORRECT: Request-scoped DB client
import { Pool } from 'pg';
export async function GET() {
// Create client within request context
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const result = await pool.query('SELECT * FROM users');
await pool.end();
return Response.json(result);
}Alternative: Use Cloudflare D1 (designed for Workers) instead of external databases:
// ✅ BEST: Use D1 (no connection pooling needed)
export async function GET(request: NextRequest) {
const env = process.env as any;
const result = await env.DB.prepare('SELECT * FROM users').all();
return Response.json(result);
}Source: https://opennext.js.org/cloudflare/troubleshooting#cannot-perform-io-on-behalf-of-a-different-request
---
5. NPM Package Import Failures
Error: "Could not resolve '<package>'"
Cause: Missing nodejs_compat flag or package export conditions
Solution 1: Enable nodejs_compat flag:
{
"compatibility_flags": ["nodejs_compat"]
}Solution 2: For packages with multiple exports, create .env:
WRANGLER_BUILD_CONDITIONS=""
WRANGLER_BUILD_PLATFORM="node"Source: https://opennext.js.org/cloudflare/troubleshooting#npm-packages-fail-to-import
---
6. Failed to Load Chunk (Turbopack)
Error: "Failed to load chunk server/chunks/ssr/"
Cause: Next.js built with Turbopack (next build --turbo)
Solution: Use standard build (Turbopack not supported by adapter):
{
"scripts": {
"build": "next build" // ✅ Correct
// "build": "next build --turbo" // ❌ Don't use Turbopack
}
}Source: https://opennext.js.org/cloudflare/troubleshooting#failed-to-load-chunk
---
7. SSRF Vulnerability (CVE-2025-6087)
Vulnerability: Server-Side Request Forgery via /_next/image endpoint
Affected versions: @opennextjs/cloudflare < 1.3.0
Solution: Upgrade to version 1.3.0 or later:
npm install --save-dev @opennextjs/cloudflare@^1.3.0Impact: Allows unauthenticated users to proxy arbitrary remote content
Source: https://github.com/advisories/GHSA-rvpw-p7vw-wj3m
---
8. Durable Objects Binding Warnings
Warning: "You have defined bindings to the following internal Durable Objects... will not work in local development, but they should work in production"
Cause: OpenNext uses Durable Objects for caching (DOQueueHandler, DOShardedTagCache)
Solution: Safe to ignore - warning is expected behavior
Alternative (to suppress warning): Define Durable Objects in separate Worker with own config
Source: https://opennext.js.org/cloudflare/known-issues#caching-durable-objects
---
9. Prisma + D1 Middleware Conflicts
Error: Build errors when using @prisma/client + @prisma/adapter-d1 in Next.js middleware
Cause: Database initialization in middleware context
Workaround: Initialize Prisma client in route handlers, not middleware
Source: https://github.com/opennextjs/opennextjs-cloudflare/issues/471
---
10. cross-fetch Library Errors
Error: Errors when using libraries that depend on cross-fetch
Cause: OpenNext patches deployment package causing cross-fetch to try using Node.js libraries when native fetch is available
Solution: Use native fetch API directly instead of cross-fetch:
// ✅ Use native fetch
const response = await fetch('https://api.example.com/data');
// ❌ Avoid cross-fetch
// import fetch from 'cross-fetch';Source: https://opennext.js.org/cloudflare/troubleshooting
---
11. Windows Development Issues
Issue: Full Windows support not guaranteed
Cause: Underlying Next.js tooling issues on Windows
Solutions:
- Use WSL (Windows Subsystem for Linux)
- Use virtual machine with Linux
- Use Linux-based CI/CD for deployments
Source: https://opennext.js.org/cloudflare#windows-support
Feature Support Matrix
| Feature | Status | Notes |
|---|---|---|
| App Router | ✅ Fully Supported | Latest App Router features work |
| Pages Router | ✅ Fully Supported | Legacy Pages Router supported |
| Route Handlers | ✅ Fully Supported | API routes work as expected |
| React Server Components | ✅ Fully Supported | RSC fully functional |
| Server Actions | ✅ Fully Supported | Server Actions work |
| SSG | ✅ Fully Supported | Static Site Generation |
| SSR | ✅ Fully Supported | Server-Side Rendering |
| ISR | ✅ Fully Supported | Incremental Static Regeneration |
| Middleware | ✅ Supported | Except Node.js middleware (15.2+) |
| Image Optimization | ✅ Supported | Via Cloudflare Images |
| Partial Prerendering (PPR) | ✅ Supported | Experimental in Next.js |
| Composable Caching | ✅ Supported | 'use cache' directive |
| Response Streaming | ✅ Supported | Streaming responses work |
| `next/after` API | ✅ Supported | Post-response async work |
| Node.js Middleware (15.2+) | ❌ Not Supported | Future support planned |
| Edge Runtime | ❌ Not Supported | Use Node.js runtime |
Source: https://developers.cloudflare.com/workers/framework-guides/web-apps/nextjs/#next-js-supported-features
Integration with Cloudflare Services
D1 Database (SQL)
// app/api/users/route.ts
import type { NextRequest } from 'next/server';
export async function GET(request: NextRequest) {
const env = process.env as any;
const result = await env.DB.prepare(
'SELECT * FROM users WHERE active = ?'
).bind(true).all();
return Response.json(result.results);
}
export async function POST(request: NextRequest) {
const env = process.env as any;
const { name, email } = await request.json();
const result = await env.DB.prepare(
'INSERT INTO users (name, email) VALUES (?, ?)'
).bind(name, email).run();
return Response.json({ id: result.meta.last_row_id });
}Wrangler config:
{
"d1_databases": [
{
"binding": "DB",
"database_name": "production-db",
"database_id": "your-database-id"
}
]
}See also: cloudflare-d1 skill for complete D1 patterns
---
R2 Storage (Object Storage)
// app/api/upload/route.ts
import type { NextRequest } from 'next/server';
export async function POST(request: NextRequest) {
const env = process.env as any;
const formData = await request.formData();
const file = formData.get('file') as File;
// Upload to R2
await env.BUCKET.put(file.name, file.stream(), {
httpMetadata: {
contentType: file.type
}
});
return Response.json({ success: true, filename: file.name });
}
export async function GET(request: NextRequest) {
const env = process.env as any;
const { searchParams } = new URL(request.url);
const filename = searchParams.get('file');
const object = await env.BUCKET.get(filename);
if (!object) {
return new Response('Not found', { status: 404 });
}
return new Response(object.body, {
headers: {
'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream'
}
});
}See also: cloudflare-r2 skill for complete R2 patterns
---
Workers AI (Model Inference)
// app/api/ai/route.ts
import type { NextRequest } from 'next/server';
export async function POST(request: NextRequest) {
const env = process.env as any;
const { prompt } = await request.json();
const response = await env.AI.run('@cf/meta/llama-3-8b-instruct', {
prompt
});
return Response.json(response);
}Wrangler config:
{
"ai": {
"binding": "AI"
}
}See also: cloudflare-workers-ai skill for complete AI patterns
---
KV Storage (Key-Value)
// app/api/cache/route.ts
import type { NextRequest } from 'next/server';
export async function GET(request: NextRequest) {
const env = process.env as any;
const { searchParams } = new URL(request.url);
const key = searchParams.get('key');
const value = await env.KV.get(key);
return Response.json({ key, value });
}
export async function PUT(request: NextRequest) {
const env = process.env as any;
const { key, value, ttl } = await request.json();
await env.KV.put(key, value, { expirationTtl: ttl });
return Response.json({ success: true });
}See also: cloudflare-kv skill for complete KV patterns
Image Optimization
Next.js image optimization works via Cloudflare Images. Configure in open-next.config.ts:
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
export default defineCloudflareConfig({
imageOptimization: {
loader: 'cloudflare'
}
});Usage in components:
import Image from 'next/image';
export default function Avatar() {
return (
<Image
src="/avatar.jpg"
alt="User avatar"
width={200}
height={200}
// Automatically optimized via Cloudflare Images
/>
);
}Billing: Cloudflare Images usage is billed separately
Docs: https://developers.cloudflare.com/images/
Caching Configuration
Configure caching behavior in open-next.config.ts:
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
export default defineCloudflareConfig({
// Custom cache configuration
cache: {
// Override default cache behavior
// See: https://opennext.js.org/cloudflare/caching
}
});Default behavior: OpenNext provides sensible caching defaults
Advanced usage: See official OpenNext caching documentation
Known Limitations
Not Yet Supported
1. Node.js Middleware (Next.js 15.2+)
- Introduced in Next.js 15.2
- Support planned for future releases
- Use standard middleware for now
2. Edge Runtime
- Only Node.js runtime supported
- Remove
export const runtime = "edge"from your app
3. Full Windows Support
- Development on Windows not fully guaranteed
- Use WSL, VM, or Linux-based CI/CD
Worker Size Constraints
- Free plan: 3 MiB limit (gzip-compressed)
- Paid plan: 10 MiB limit (gzip-compressed)
- Monitor bundle size during development
- Use dynamic imports for code splitting
Database Connections
- External database clients (PostgreSQL, MySQL) must be request-scoped
- Cannot reuse connections across requests (Workers limitation)
- Prefer Cloudflare D1 for database needs (designed for Workers)
Deployment
Deploy from Local Machine
# Build and deploy in one command
npm run deploy
# Or step by step:
npx opennextjs-cloudflare build
npx opennextjs-cloudflare deployDeploy from CI/CD
Configure deployment command in your CI/CD system:
npm run deployExamples:
- GitHub Actions:
.github/workflows/deploy.yml - GitLab CI:
.gitlab-ci.yml - Cloudflare Workers Builds: Auto-detects
npm run deploy
Environment variables: Set secrets in Cloudflare dashboard or CI/CD system
Custom Domains
Add custom domain in Cloudflare dashboard:
1. Navigate to Workers & Pages 2. Select your Worker 3. Settings → Domains & Routes 4. Add custom domain
DNS: Domain must be on Cloudflare (zone required)
TypeScript Support
Generate types for Cloudflare bindings:
npm run cf-typegenCreates cloudflare-env.d.ts with types for your bindings:
// cloudflare-env.d.ts (auto-generated)
interface CloudflareEnv {
DB: D1Database;
BUCKET: R2Bucket;
KV: KVNamespace;
AI: Ai;
}Use in route handlers:
import type { NextRequest } from 'next/server';
export async function GET(request: NextRequest) {
const env = process.env as CloudflareEnv;
// Now env.DB, env.BUCKET, etc. are typed
}Testing
Local Testing (Development)
# Next.js dev server (fast iteration)
npm run devLocal Testing (Production-like)
# Workerd runtime (catches Workers-specific issues)
npm run previewIntegration Testing
Always test in preview mode before deploying:
# Build and run in workerd
npm run preview
# Test bindings (D1, R2, KV, AI)
# Test middleware
# Test API routes
# Test SSR/ISR behaviorMigration from Other Platforms
From Vercel
1. Copy existing Next.js project 2. Run existing project migration steps (above) 3. Update environment variables in Cloudflare dashboard 4. Replace Vercel-specific features:
- Vercel Postgres → Cloudflare D1
- Vercel Blob → Cloudflare R2
- Vercel KV → Cloudflare KV
- Vercel Edge Config → Cloudflare KV
5. Test thoroughly with npm run preview 6. Deploy with npm run deploy
From AWS / Other Platforms
Same process as Vercel migration - the adapter handles Next.js standard features automatically.
Resources
Official Documentation
- OpenNext Cloudflare: https://opennext.js.org/cloudflare
- Cloudflare Next.js Guide: https://developers.cloudflare.com/workers/framework-guides/web-apps/nextjs/
- Next.js Docs: https://nextjs.org/docs
Troubleshooting
- Troubleshooting Guide: https://opennext.js.org/cloudflare/troubleshooting
- Known Issues: https://opennext.js.org/cloudflare/known-issues
- GitHub Issues: https://github.com/opennextjs/opennextjs-cloudflare/issues
Related Skills
cloudflare-worker-base- Base Worker setup with Hono + Vite + Reactcloudflare-d1- D1 database integrationcloudflare-r2- R2 object storagecloudflare-kv- KV key-value storagecloudflare-workers-ai- Workers AI integrationcloudflare-vectorize- Vector database for RAG
Quick Reference
Essential Commands
# New project
npm create cloudflare@latest -- my-next-app --framework=next
# Development
npm run dev # Fast iteration (Next.js dev server)
npm run preview # Test in workerd (production-like)
# Deployment
npm run deploy # Build and deploy to Cloudflare
# TypeScript
npm run cf-typegen # Generate binding typesCritical Configuration
// wrangler.jsonc
{
"compatibility_date": "2025-05-05", // Minimum!
"compatibility_flags": ["nodejs_compat"] // Required!
}Common Pitfalls
1. ❌ Using Edge runtime → ✅ Use Node.js runtime 2. ❌ Global DB clients → ✅ Request-scoped clients 3. ❌ Old compatibility_date → ✅ Use 2025-05-05+ 4. ❌ Missing nodejs_compat → ✅ Add to compatibility_flags 5. ❌ Only testing in dev → ✅ Always test preview before deploy 6. ❌ Using Turbopack → ✅ Use standard Next.js build
---
Production Tested: Official Cloudflare support and active community Token Savings: ~59% vs manual setup Errors Prevented: 10+ documented issues Last Verified: 2025-10-21
Cloudflare Next.js Deployment Skill
Deploy Next.js applications to Cloudflare Workers using the OpenNext adapter for production-ready serverless Next.js hosting.
Auto-Trigger Keywords
This skill should be automatically discovered when the user mentions:
Primary Keywords
- next.js cloudflare
- nextjs workers
- deploy next.js to cloudflare
- opennext adapter
- opennext cloudflare
- next.js on workers
- cloudflare next app
Framework Features
- next.js app router cloudflare
- next.js pages router workers
- next.js ssr cloudflare
- next.js isr workers
- server components cloudflare
- server actions workers
- next.js middleware cloudflare
Migration Keywords
- migrate next.js to cloudflare
- vercel to cloudflare nextjs
- next.js serverless cloudflare
- next.js edge cloudflare
Integration Keywords
- next.js d1 database
- next.js r2 storage
- next.js workers ai
- next.js cloudflare kv
- next.js cloudflare images
Error-Related Keywords
- worker size limit nextjs
- finalizationregistry nextjs
- cannot perform i/o nextjs
- nextjs turbopack cloudflare
- opennext errors
- nextjs workers compatibility
What This Skill Covers
Setup & Configuration
- ✅ New Next.js project scaffolding with C3
- ✅ Existing Next.js project migration
- ✅ Wrangler configuration (compatibility_date, compatibility_flags)
- ✅ OpenNext config setup and caching
- ✅ Package.json scripts for dev/preview/deploy
Development Workflow
- ✅ Dual testing strategy (Next.js dev server + workerd preview)
- ✅ Local development best practices
- ✅ Production-like testing before deployment
- ✅ TypeScript types generation for bindings
Cloudflare Integration
- ✅ D1 Database access from Next.js
- ✅ R2 Storage integration
- ✅ KV storage patterns
- ✅ Workers AI inference
- ✅ Image optimization via Cloudflare Images
- ✅ Custom domains setup
Error Prevention (10+ Documented Errors)
- ✅ Worker size limit errors (3 MiB / 10 MiB)
- ✅ FinalizationRegistry compatibility
- ✅ Database connection scoping
- ✅ Package import failures
- ✅ Turbopack build errors
- ✅ SSRF vulnerability (CVE-2025-6087)
- ✅ Durable Objects warnings
- ✅ Prisma + D1 conflicts
- ✅ cross-fetch library issues
- ✅ Windows development caveats
Feature Support
- ✅ App Router and Pages Router
- ✅ SSR, SSG, and ISR
- ✅ React Server Components
- ✅ Server Actions
- ✅ Route Handlers
- ✅ Middleware (with limitations)
- ✅ Image optimization
- ✅ Partial Prerendering (PPR)
- ✅ Composable Caching
When to Use This Skill
Use this skill when:
1. Deploying Next.js to Cloudflare Workers
- New Next.js applications
- Migrating existing Next.js apps from Vercel/AWS/other platforms
2. Need Next.js Features on Workers
- Server-side rendering (SSR)
- Static site generation (SSG)
- Incremental static regeneration (ISR)
- React Server Components
- Server Actions
3. Integrating with Cloudflare Services
- D1 Database queries
- R2 object storage
- KV key-value storage
- Workers AI inference
- Cloudflare Images
4. Troubleshooting Next.js on Workers
- Worker size limit errors
- Runtime compatibility issues
- Database connection problems
- Build/deployment errors
When NOT to Use This Skill
Don't use this skill if:
1. Building with Vite + React (not Next.js) → Use cloudflare-worker-base skill instead
2. Deploying to Cloudflare Pages (not Workers) → This skill is specifically for Workers deployment
3. Using static export only (no SSR/ISR) → Consider simpler Workers Static Assets setup
4. Working with other frameworks (Remix, SvelteKit, etc.) → Refer to framework-specific guides
Related Skills
- cloudflare-worker-base - Base Worker setup with Hono + Vite + React (use for non-Next.js React apps)
- cloudflare-d1 - D1 database integration patterns
- cloudflare-r2 - R2 object storage patterns
- cloudflare-kv - KV key-value storage patterns
- cloudflare-workers-ai - Workers AI inference patterns
- cloudflare-vectorize - Vector database for RAG applications
Quick Start
New Project
npm create cloudflare@latest -- my-next-app --framework=next
cd my-next-app
npm run dev # Development
npm run preview # Test in workerd
npm run deploy # Deploy to CloudflareExisting Project
npm install --save-dev @opennextjs/cloudflare
# Create wrangler.jsonc and open-next.config.ts
# Update package.json scripts
npm run preview # Test before deploying
npm run deploy # DeployResources Included
Scripts
setup-new-project.sh- Scaffold new Next.js project with C3setup-existing-project.sh- Add OpenNext adapter to existing projectanalyze-bundle.sh- Debug worker size issues
Templates
wrangler.jsonc- Complete wrangler configurationopen-next.config.ts- OpenNext adapter configpackage.json- Scripts for dev/preview/deploy.env- Environment variables for package exports
Documentation
troubleshooting.md- All common errors and solutionsfeature-support.md- Feature compatibility matrixworkflow-diagram.md- Development workflow visualization
Key Differences from Standard Next.js
| Aspect | Standard Next.js | Cloudflare Workers |
|---|---|---|
| Runtime | Node.js or Edge | Node.js (via nodejs_compat) |
| Dev Server | next dev only | next dev + opennextjs-cloudflare preview |
| Worker Size | No limit | 3 MiB (free) / 10 MiB (paid) |
| DB Connections | Global clients OK | Must be request-scoped |
| Image Optimization | Built-in | Via Cloudflare Images |
Critical Configuration Requirements
// wrangler.jsonc (MINIMUM)
{
"compatibility_date": "2025-05-05", // Required for FinalizationRegistry
"compatibility_flags": ["nodejs_compat"] // Required for Node.js runtime
}Token Efficiency
Estimated Token Savings: ~59%
| Scenario | Without Skill | With Skill | Savings |
|---|---|---|---|
| New project setup | ~15k tokens | ~6k tokens | ~60% |
| Existing migration | ~18k tokens | ~7k tokens | ~61% |
| Troubleshooting | ~10k tokens | ~3k tokens | ~70% |
Errors Prevented: 10+ documented issues with sources and solutions
Version Information
Package Versions (verified 2025-10-21):
@opennextjs/cloudflare: ^1.3.0 (security fix for CVE-2025-6087)next: ^14.2.0 || ^15.0.0wrangler: latest
Compatibility Requirements:
compatibility_date: 2025-05-05 minimumcompatibility_flags: ["nodejs_compat"]
Next.js Version Support:
- Next.js 14.x (latest minor release)
- Next.js 15.x (all minor/patch versions)
Official Documentation
- OpenNext Cloudflare: https://opennext.js.org/cloudflare
- Cloudflare Next.js Guide: https://developers.cloudflare.com/workers/framework-guides/web-apps/nextjs/
- Troubleshooting: https://opennext.js.org/cloudflare/troubleshooting
- Known Issues: https://opennext.js.org/cloudflare/known-issues
- GitHub: https://github.com/opennextjs/opennextjs-cloudflare
Production Status
✅ Production Ready
- Official Cloudflare support
- Active maintenance and community
- Security updates (latest: CVE-2025-6087 fix in v1.3.0)
- Comprehensive documentation
- Tested with Next.js 14.x and 15.x
---
Last Updated: 2025-10-21 Skill Version: 1.0.0 License: MIT
{
"description": "|",
"metadata": {
"license": "MIT"
},
"references": {
"files": [
"references/feature-support.md",
"references/troubleshooting.md"
]
},
"content": "Deploy Next.js applications to Cloudflare Workers using the OpenNext Cloudflare adapter for production-ready serverless Next.js hosting.\r\n\r\n\r\n### Dual Testing Strategy\r\n\r\n**Always test in BOTH environments**:\r\n\r\n1. **Next.js Dev Server** (`npm run dev`)\r\n - Fast hot reloading\r\n - Best developer experience\r\n - Runs in Node.js (not production runtime)\r\n - Use for rapid iteration\r\n\r\n2. **Workerd Runtime** (`npm run preview`)\r\n - Runs in production-like environment\r\n - Catches runtime-specific issues\r\n - Slower rebuild times\r\n - **Required before deployment**\r\n\r\n### When to Use Each\r\n\r\n```bash\r\nnpm run dev\r\n\r\nnpm run preview\r\n\r\nnpm run preview\r\n\r\n\r\n### 1. Worker Size Limit Exceeded (3 MiB - Free Plan)\r\n\r\n**Error**: `\"Your Worker exceeded the size limit of 3 MiB\"`\r\n\r\n**Cause**: Workers Free plan limits Worker size to 3 MiB (gzip-compressed)\r\n\r\n**Solutions**:\r\n- Upgrade to Workers Paid plan (10 MiB limit)\r\n- Analyze bundle size and remove unused dependencies\r\n- Use dynamic imports to code-split large dependencies\r\n\r\n**Bundle analysis**:\r\n```bash\r\nnpx opennextjs-cloudflare build\r\ncd .open-next/server-functions/default\r\n\r\n### Deploy from Local Machine\r\n\r\n```bash\r\nnpm run deploy\r\n\r\n\r\n### Local Testing (Development)\r\n\r\n```bash\r\nnpm run dev\r\n```\r\n\r\n### Local Testing (Production-like)\r\n\r\n```bash\r\nnpm run preview\r\n```\r\n\r\n### Integration Testing\r\n\r\nAlways test in `preview` mode before deploying:\r\n\r\n```bash\r\nnpm run preview\r\n\r\n\r\n### Essential Commands\r\n\r\n```bash\r\nnpm create cloudflare@latest -- my-next-app --framework=next\r\n\r\nnpm run dev # Fast iteration (Next.js dev server)\r\nnpm run preview # Test in workerd (production-like)\r\n\r\nnpm run deploy # Build and deploy to Cloudflare",
"name": "cloudflare-nextjs",
"id": "cloudflare-nextjs",
"sections": {
"Migration from Other Platforms": "### From Vercel\r\n\r\n1. Copy existing Next.js project\r\n2. Run existing project migration steps (above)\r\n3. Update environment variables in Cloudflare dashboard\r\n4. Replace Vercel-specific features:\r\n - Vercel Postgres → Cloudflare D1\r\n - Vercel Blob → Cloudflare R2\r\n - Vercel KV → Cloudflare KV\r\n - Vercel Edge Config → Cloudflare KV\r\n5. Test thoroughly with `npm run preview`\r\n6. Deploy with `npm run deploy`\r\n\r\n### From AWS / Other Platforms\r\n\r\nSame process as Vercel migration - the adapter handles Next.js standard features automatically.",
"Configuration Requirements": "### Wrangler Configuration\r\n\r\n**Minimum requirements** in `wrangler.jsonc`:\r\n\r\n```jsonc\r\n{\r\n \"name\": \"your-app-name\",\r\n \"compatibility_date\": \"2025-05-05\", // Minimum for FinalizationRegistry\r\n \"compatibility_flags\": [\"nodejs_compat\"] // Required for Node.js runtime\r\n}\r\n```\r\n\r\n### Environment Variables for Package Exports\r\n\r\nIf using npm packages with multiple export conditions, create `.env`:\r\n\r\n```env\r\nWRANGLER_BUILD_CONDITIONS=\"\"\r\nWRANGLER_BUILD_PLATFORM=\"node\"\r\n```\r\n\r\nThis ensures Wrangler prioritizes the `node` export when available.\r\n\r\n### Cloudflare Bindings Integration\r\n\r\nAdd bindings in `wrangler.jsonc`:\r\n\r\n```jsonc\r\n{\r\n \"name\": \"your-app-name\",\r\n \"compatibility_date\": \"2025-05-05\",\r\n \"compatibility_flags\": [\"nodejs_compat\"],\r\n\r\n // D1 Database\r\n \"d1_databases\": [\r\n {\r\n \"binding\": \"DB\",\r\n \"database_name\": \"production-db\",\r\n \"database_id\": \"your-database-id\"\r\n }\r\n ],\r\n\r\n // R2 Storage\r\n \"r2_buckets\": [\r\n {\r\n \"binding\": \"BUCKET\",\r\n \"bucket_name\": \"your-bucket\"\r\n }\r\n ],\r\n\r\n // KV Storage\r\n \"kv_namespaces\": [\r\n {\r\n \"binding\": \"KV\",\r\n \"id\": \"your-kv-id\"\r\n }\r\n ],\r\n\r\n // Workers AI\r\n \"ai\": {\r\n \"binding\": \"AI\"\r\n }\r\n}\r\n```\r\n\r\nAccess bindings in Next.js via `process.env`:\r\n\r\n```typescript\r\n// app/api/route.ts\r\nimport type { NextRequest } from 'next/server';\r\n\r\nexport async function GET(request: NextRequest) {\r\n // Access Cloudflare bindings\r\n const env = process.env as any;\r\n\r\n // D1 Database query\r\n const result = await env.DB.prepare('SELECT * FROM users').all();\r\n\r\n // R2 Storage access\r\n const file = await env.BUCKET.get('file.txt');\r\n\r\n // KV Storage access\r\n const value = await env.KV.get('key');\r\n\r\n // Workers AI inference\r\n const response = await env.AI.run('@cf/meta/llama-3-8b-instruct', {\r\n prompt: 'Hello AI'\r\n });\r\n\r\n return Response.json({ result });\r\n}\r\n```",
"Setup Patterns": "### New Project Setup\r\n\r\nUse Cloudflare's `create-cloudflare` (C3) CLI to scaffold a new Next.js project pre-configured for Workers:\r\n\r\n```bash\r\nnpm create cloudflare@latest -- my-next-app --framework=next\r\n```\r\n\r\n**What this does**:\r\n1. Runs Next.js official setup tool (`create-next-app`)\r\n2. Installs `@opennextjs/cloudflare` adapter\r\n3. Creates `wrangler.jsonc` with correct configuration\r\n4. Creates `open-next.config.ts` for caching configuration\r\n5. Adds deployment scripts to `package.json`\r\n6. Optionally deploys immediately to Cloudflare\r\n\r\n**Development workflow**:\r\n```bash\r\nnpm run dev # Next.js dev server (fast reloads)\r\nnpm run preview # Test in workerd runtime (production-like)\r\nnpm run deploy # Build and deploy to Cloudflare\r\n```\r\n\r\n### Existing Project Migration\r\n\r\nTo add the OpenNext adapter to an existing Next.js application:\r\n\r\n#### 1. Install the adapter\r\n\r\n```bash\r\nnpm install --save-dev @opennextjs/cloudflare\r\n```\r\n\r\n#### 2. Create wrangler.jsonc\r\n\r\n```jsonc\r\n{\r\n \"name\": \"my-next-app\",\r\n \"compatibility_date\": \"2025-05-05\",\r\n \"compatibility_flags\": [\"nodejs_compat\"]\r\n}\r\n```\r\n\r\n**Critical configuration**:\r\n- `compatibility_date`: **Minimum `2025-05-05`** (for FinalizationRegistry support)\r\n- `compatibility_flags`: **Must include `nodejs_compat`** (for Node.js runtime)\r\n\r\n#### 3. Create open-next.config.ts\r\n\r\n```typescript\r\nimport { defineCloudflareConfig } from \"@opennextjs/cloudflare\";\r\n\r\nexport default defineCloudflareConfig({\r\n // Caching configuration (optional)\r\n // See: https://opennext.js.org/cloudflare/caching\r\n});\r\n```\r\n\r\n#### 4. Update package.json scripts\r\n\r\n```json\r\n{\r\n \"scripts\": {\r\n \"dev\": \"next dev\",\r\n \"build\": \"next build\",\r\n \"preview\": \"opennextjs-cloudflare build && opennextjs-cloudflare preview\",\r\n \"deploy\": \"opennextjs-cloudflare build && opennextjs-cloudflare deploy\",\r\n \"cf-typegen\": \"wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts\"\r\n }\r\n}\r\n```\r\n\r\n**Script purposes**:\r\n- `dev`: Next.js development server (fast iteration)\r\n- `preview`: Build + run in workerd runtime (test before deploy)\r\n- `deploy`: Build + deploy to Cloudflare\r\n- `cf-typegen`: Generate TypeScript types for Cloudflare bindings\r\n\r\n#### 5. Ensure Node.js runtime (not Edge)\r\n\r\nRemove Edge runtime exports from your app:\r\n\r\n```typescript\r\n// ❌ REMOVE THIS (Edge runtime not supported)\r\nexport const runtime = \"edge\";\r\n\r\n// ✅ Use Node.js runtime (default)\r\n// No export needed - Node.js is default\r\n```",
"Key Concepts": "### OpenNext Adapter Architecture\r\n\r\nThe **OpenNext Cloudflare adapter** (`@opennextjs/cloudflare`) transforms Next.js build output into Cloudflare Worker-compatible format. This is fundamentally different from standard Next.js deployments:\r\n\r\n- **Node.js Runtime Required**: Uses Node.js runtime in Workers (NOT Edge runtime)\r\n- **Dual Development Workflow**: Test in both Next.js dev server AND workerd runtime\r\n- **Custom Build Pipeline**: `next build` → OpenNext transformation → Worker deployment\r\n- **Cloudflare-Specific Configuration**: Requires wrangler.jsonc and open-next.config.ts\r\n\r\n### Critical Differences from Standard Next.js\r\n\r\n| Aspect | Standard Next.js | Cloudflare Workers |\r\n|--------|------------------|-------------------|\r\n| Runtime | Node.js or Edge | Node.js (via nodejs_compat) |\r\n| Dev Server | `next dev` | `next dev` + `opennextjs-cloudflare preview` |\r\n| Deployment | Platform-specific | `opennextjs-cloudflare deploy` |\r\n| Worker Size | No limit | 3 MiB (free) / 10 MiB (paid) |\r\n| Database Connections | Global clients OK | Must be request-scoped |\r\n| Image Optimization | Built-in | Via Cloudflare Images |\r\n| Caching | Next.js cache | OpenNext config + Workers cache |",
"TypeScript Support": "Generate types for Cloudflare bindings:\r\n\r\n```bash\r\nnpm run cf-typegen\r\n```\r\n\r\nCreates `cloudflare-env.d.ts` with types for your bindings:\r\n\r\n```typescript\r\n// cloudflare-env.d.ts (auto-generated)\r\ninterface CloudflareEnv {\r\n DB: D1Database;\r\n BUCKET: R2Bucket;\r\n KV: KVNamespace;\r\n AI: Ai;\r\n}\r\n```\r\n\r\nUse in route handlers:\r\n\r\n```typescript\r\nimport type { NextRequest } from 'next/server';\r\n\r\nexport async function GET(request: NextRequest) {\r\n const env = process.env as CloudflareEnv;\r\n // Now env.DB, env.BUCKET, etc. are typed\r\n}\r\n```",
"Feature Support Matrix": "| Feature | Status | Notes |\r\n|---------|--------|-------|\r\n| **App Router** | ✅ Fully Supported | Latest App Router features work |\r\n| **Pages Router** | ✅ Fully Supported | Legacy Pages Router supported |\r\n| **Route Handlers** | ✅ Fully Supported | API routes work as expected |\r\n| **React Server Components** | ✅ Fully Supported | RSC fully functional |\r\n| **Server Actions** | ✅ Fully Supported | Server Actions work |\r\n| **SSG** | ✅ Fully Supported | Static Site Generation |\r\n| **SSR** | ✅ Fully Supported | Server-Side Rendering |\r\n| **ISR** | ✅ Fully Supported | Incremental Static Regeneration |\r\n| **Middleware** | ✅ Supported | Except Node.js middleware (15.2+) |\r\n| **Image Optimization** | ✅ Supported | Via Cloudflare Images |\r\n| **Partial Prerendering (PPR)** | ✅ Supported | Experimental in Next.js |\r\n| **Composable Caching** | ✅ Supported | `'use cache'` directive |\r\n| **Response Streaming** | ✅ Supported | Streaming responses work |\r\n| **`next/after` API** | ✅ Supported | Post-response async work |\r\n| **Node.js Middleware (15.2+)** | ❌ Not Supported | Future support planned |\r\n| **Edge Runtime** | ❌ Not Supported | Use Node.js runtime |\r\n\r\n**Source**: https://developers.cloudflare.com/workers/framework-guides/web-apps/nextjs/#next-js-supported-features",
"Testing": "```",
"Image Optimization": "Next.js image optimization works via Cloudflare Images. Configure in `open-next.config.ts`:\r\n\r\n```typescript\r\nimport { defineCloudflareConfig } from \"@opennextjs/cloudflare\";\r\n\r\nexport default defineCloudflareConfig({\r\n imageOptimization: {\r\n loader: 'cloudflare'\r\n }\r\n});\r\n```\r\n\r\nUsage in components:\r\n\r\n```tsx\r\nimport Image from 'next/image';\r\n\r\nexport default function Avatar() {\r\n return (\r\n <Image\r\n src=\"/avatar.jpg\"\r\n alt=\"User avatar\"\r\n width={200}\r\n height={200}\r\n // Automatically optimized via Cloudflare Images\r\n />\r\n );\r\n}\r\n```\r\n\r\n**Billing**: Cloudflare Images usage is billed separately\r\n\r\n**Docs**: https://developers.cloudflare.com/images/",
"Deployment": "npx opennextjs-cloudflare build\r\nnpx opennextjs-cloudflare deploy\r\n```\r\n\r\n### Deploy from CI/CD\r\n\r\nConfigure deployment command in your CI/CD system:\r\n\r\n```bash\r\nnpm run deploy\r\n```\r\n\r\n**Examples**:\r\n- GitHub Actions: `.github/workflows/deploy.yml`\r\n- GitLab CI: `.gitlab-ci.yml`\r\n- Cloudflare Workers Builds: Auto-detects `npm run deploy`\r\n\r\n**Environment variables**: Set secrets in Cloudflare dashboard or CI/CD system\r\n\r\n### Custom Domains\r\n\r\nAdd custom domain in Cloudflare dashboard:\r\n\r\n1. Navigate to Workers & Pages\r\n2. Select your Worker\r\n3. Settings → Domains & Routes\r\n4. Add custom domain\r\n\r\n**DNS**: Domain must be on Cloudflare (zone required)",
"Development Workflow": "npm run deploy\r\n```",
"Known Limitations": "### Not Yet Supported\r\n\r\n1. **Node.js Middleware (Next.js 15.2+)**\r\n - Introduced in Next.js 15.2\r\n - Support planned for future releases\r\n - Use standard middleware for now\r\n\r\n2. **Edge Runtime**\r\n - Only Node.js runtime supported\r\n - Remove `export const runtime = \"edge\"` from your app\r\n\r\n3. **Full Windows Support**\r\n - Development on Windows not fully guaranteed\r\n - Use WSL, VM, or Linux-based CI/CD\r\n\r\n### Worker Size Constraints\r\n\r\n- **Free plan**: 3 MiB limit (gzip-compressed)\r\n- **Paid plan**: 10 MiB limit (gzip-compressed)\r\n- Monitor bundle size during development\r\n- Use dynamic imports for code splitting\r\n\r\n### Database Connections\r\n\r\n- External database clients (PostgreSQL, MySQL) must be request-scoped\r\n- Cannot reuse connections across requests (Workers limitation)\r\n- Prefer Cloudflare D1 for database needs (designed for Workers)",
"Resources": "### Official Documentation\r\n- **OpenNext Cloudflare**: https://opennext.js.org/cloudflare\r\n- **Cloudflare Next.js Guide**: https://developers.cloudflare.com/workers/framework-guides/web-apps/nextjs/\r\n- **Next.js Docs**: https://nextjs.org/docs\r\n\r\n### Troubleshooting\r\n- **Troubleshooting Guide**: https://opennext.js.org/cloudflare/troubleshooting\r\n- **Known Issues**: https://opennext.js.org/cloudflare/known-issues\r\n- **GitHub Issues**: https://github.com/opennextjs/opennextjs-cloudflare/issues\r\n\r\n### Related Skills\r\n- `cloudflare-worker-base` - Base Worker setup with Hono + Vite + React\r\n- `cloudflare-d1` - D1 database integration\r\n- `cloudflare-r2` - R2 object storage\r\n- `cloudflare-kv` - KV key-value storage\r\n- `cloudflare-workers-ai` - Workers AI integration\r\n- `cloudflare-vectorize` - Vector database for RAG",
"Error Prevention (10+ Documented Errors)": "```\r\n\r\n**Source**: https://opennext.js.org/cloudflare/troubleshooting#worker-size-limits\r\n\r\n---\r\n\r\n### 2. Worker Size Limit Exceeded (10 MiB - Paid Plan)\r\n\r\n**Error**: `\"Your Worker exceeded the size limit of 10 MiB\"`\r\n\r\n**Cause**: Unnecessary code bundled into Worker\r\n\r\n**Debug workflow**:\r\n1. Run `npx opennextjs-cloudflare build`\r\n2. Navigate to `.open-next/server-functions/default`\r\n3. Analyze `handler.mjs.meta.json` using ESBuild Bundle Analyzer\r\n4. Identify and remove/externalize large dependencies\r\n\r\n**Source**: https://opennext.js.org/cloudflare/troubleshooting#worker-size-limits\r\n\r\n---\r\n\r\n### 3. FinalizationRegistry Not Defined\r\n\r\n**Error**: `\"ReferenceError: FinalizationRegistry is not defined\"`\r\n\r\n**Cause**: `compatibility_date` in wrangler.jsonc is too old\r\n\r\n**Solution**: Update `compatibility_date` to `2025-05-05` or later:\r\n\r\n```jsonc\r\n{\r\n \"compatibility_date\": \"2025-05-05\" // Minimum for FinalizationRegistry\r\n}\r\n```\r\n\r\n**Source**: https://opennext.js.org/cloudflare/troubleshooting#finalizationregistry-is-not-defined\r\n\r\n---\r\n\r\n### 4. Cannot Perform I/O on Behalf of Different Request\r\n\r\n**Error**: `\"Cannot perform I/O on behalf of a different request\"`\r\n\r\n**Cause**: Database client created globally and reused across requests\r\n\r\n**Problem code**:\r\n```typescript\r\n// ❌ WRONG: Global DB client\r\nimport { Pool } from 'pg';\r\nconst pool = new Pool({ connectionString: process.env.DATABASE_URL });\r\n\r\nexport async function GET() {\r\n // This will fail - pool created in different request context\r\n const result = await pool.query('SELECT * FROM users');\r\n return Response.json(result);\r\n}\r\n```\r\n\r\n**Solution**: Create database clients inside request handlers:\r\n\r\n```typescript\r\n// ✅ CORRECT: Request-scoped DB client\r\nimport { Pool } from 'pg';\r\n\r\nexport async function GET() {\r\n // Create client within request context\r\n const pool = new Pool({ connectionString: process.env.DATABASE_URL });\r\n const result = await pool.query('SELECT * FROM users');\r\n await pool.end();\r\n return Response.json(result);\r\n}\r\n```\r\n\r\n**Alternative**: Use Cloudflare D1 (designed for Workers) instead of external databases:\r\n\r\n```typescript\r\n// ✅ BEST: Use D1 (no connection pooling needed)\r\nexport async function GET(request: NextRequest) {\r\n const env = process.env as any;\r\n const result = await env.DB.prepare('SELECT * FROM users').all();\r\n return Response.json(result);\r\n}\r\n```\r\n\r\n**Source**: https://opennext.js.org/cloudflare/troubleshooting#cannot-perform-io-on-behalf-of-a-different-request\r\n\r\n---\r\n\r\n### 5. NPM Package Import Failures\r\n\r\n**Error**: `\"Could not resolve '<package>'\"`\r\n\r\n**Cause**: Missing `nodejs_compat` flag or package export conditions\r\n\r\n**Solution 1**: Enable `nodejs_compat` flag:\r\n\r\n```jsonc\r\n{\r\n \"compatibility_flags\": [\"nodejs_compat\"]\r\n}\r\n```\r\n\r\n**Solution 2**: For packages with multiple exports, create `.env`:\r\n\r\n```env\r\nWRANGLER_BUILD_CONDITIONS=\"\"\r\nWRANGLER_BUILD_PLATFORM=\"node\"\r\n```\r\n\r\n**Source**: https://opennext.js.org/cloudflare/troubleshooting#npm-packages-fail-to-import\r\n\r\n---\r\n\r\n### 6. Failed to Load Chunk (Turbopack)\r\n\r\n**Error**: `\"Failed to load chunk server/chunks/ssr/\"`\r\n\r\n**Cause**: Next.js built with Turbopack (`next build --turbo`)\r\n\r\n**Solution**: Use standard build (Turbopack not supported by adapter):\r\n\r\n```json\r\n{\r\n \"scripts\": {\r\n \"build\": \"next build\" // ✅ Correct\r\n // \"build\": \"next build --turbo\" // ❌ Don't use Turbopack\r\n }\r\n}\r\n```\r\n\r\n**Source**: https://opennext.js.org/cloudflare/troubleshooting#failed-to-load-chunk\r\n\r\n---\r\n\r\n### 7. SSRF Vulnerability (CVE-2025-6087)\r\n\r\n**Vulnerability**: Server-Side Request Forgery via `/_next/image` endpoint\r\n\r\n**Affected versions**: `@opennextjs/cloudflare` < 1.3.0\r\n\r\n**Solution**: Upgrade to version 1.3.0 or later:\r\n\r\n```bash\r\nnpm install --save-dev @opennextjs/cloudflare@^1.3.0\r\n```\r\n\r\n**Impact**: Allows unauthenticated users to proxy arbitrary remote content\r\n\r\n**Source**: https://github.com/advisories/GHSA-rvpw-p7vw-wj3m\r\n\r\n---\r\n\r\n### 8. Durable Objects Binding Warnings\r\n\r\n**Warning**: `\"You have defined bindings to the following internal Durable Objects... will not work in local development, but they should work in production\"`\r\n\r\n**Cause**: OpenNext uses Durable Objects for caching (`DOQueueHandler`, `DOShardedTagCache`)\r\n\r\n**Solution**: **Safe to ignore** - warning is expected behavior\r\n\r\n**Alternative** (to suppress warning): Define Durable Objects in separate Worker with own config\r\n\r\n**Source**: https://opennext.js.org/cloudflare/known-issues#caching-durable-objects\r\n\r\n---\r\n\r\n### 9. Prisma + D1 Middleware Conflicts\r\n\r\n**Error**: Build errors when using `@prisma/client` + `@prisma/adapter-d1` in Next.js middleware\r\n\r\n**Cause**: Database initialization in middleware context\r\n\r\n**Workaround**: Initialize Prisma client in route handlers, not middleware\r\n\r\n**Source**: https://github.com/opennextjs/opennextjs-cloudflare/issues/471\r\n\r\n---\r\n\r\n### 10. cross-fetch Library Errors\r\n\r\n**Error**: Errors when using libraries that depend on `cross-fetch`\r\n\r\n**Cause**: OpenNext patches deployment package causing `cross-fetch` to try using Node.js libraries when native fetch is available\r\n\r\n**Solution**: Use native `fetch` API directly instead of `cross-fetch`:\r\n\r\n```typescript\r\n// ✅ Use native fetch\r\nconst response = await fetch('https://api.example.com/data');\r\n\r\n// ❌ Avoid cross-fetch\r\n// import fetch from 'cross-fetch';\r\n```\r\n\r\n**Source**: https://opennext.js.org/cloudflare/troubleshooting\r\n\r\n---\r\n\r\n### 11. Windows Development Issues\r\n\r\n**Issue**: Full Windows support not guaranteed\r\n\r\n**Cause**: Underlying Next.js tooling issues on Windows\r\n\r\n**Solutions**:\r\n- Use WSL (Windows Subsystem for Linux)\r\n- Use virtual machine with Linux\r\n- Use Linux-based CI/CD for deployments\r\n\r\n**Source**: https://opennext.js.org/cloudflare#windows-support",
"Integration with Cloudflare Services": "### D1 Database (SQL)\r\n\r\n```typescript\r\n// app/api/users/route.ts\r\nimport type { NextRequest } from 'next/server';\r\n\r\nexport async function GET(request: NextRequest) {\r\n const env = process.env as any;\r\n\r\n const result = await env.DB.prepare(\r\n 'SELECT * FROM users WHERE active = ?'\r\n ).bind(true).all();\r\n\r\n return Response.json(result.results);\r\n}\r\n\r\nexport async function POST(request: NextRequest) {\r\n const env = process.env as any;\r\n const { name, email } = await request.json();\r\n\r\n const result = await env.DB.prepare(\r\n 'INSERT INTO users (name, email) VALUES (?, ?)'\r\n ).bind(name, email).run();\r\n\r\n return Response.json({ id: result.meta.last_row_id });\r\n}\r\n```\r\n\r\n**Wrangler config**:\r\n```jsonc\r\n{\r\n \"d1_databases\": [\r\n {\r\n \"binding\": \"DB\",\r\n \"database_name\": \"production-db\",\r\n \"database_id\": \"your-database-id\"\r\n }\r\n ]\r\n}\r\n```\r\n\r\n**See also**: `cloudflare-d1` skill for complete D1 patterns\r\n\r\n---\r\n\r\n### R2 Storage (Object Storage)\r\n\r\n```typescript\r\n// app/api/upload/route.ts\r\nimport type { NextRequest } from 'next/server';\r\n\r\nexport async function POST(request: NextRequest) {\r\n const env = process.env as any;\r\n const formData = await request.formData();\r\n const file = formData.get('file') as File;\r\n\r\n // Upload to R2\r\n await env.BUCKET.put(file.name, file.stream(), {\r\n httpMetadata: {\r\n contentType: file.type\r\n }\r\n });\r\n\r\n return Response.json({ success: true, filename: file.name });\r\n}\r\n\r\nexport async function GET(request: NextRequest) {\r\n const env = process.env as any;\r\n const { searchParams } = new URL(request.url);\r\n const filename = searchParams.get('file');\r\n\r\n const object = await env.BUCKET.get(filename);\r\n if (!object) {\r\n return new Response('Not found', { status: 404 });\r\n }\r\n\r\n return new Response(object.body, {\r\n headers: {\r\n 'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream'\r\n }\r\n });\r\n}\r\n```\r\n\r\n**See also**: `cloudflare-r2` skill for complete R2 patterns\r\n\r\n---\r\n\r\n### Workers AI (Model Inference)\r\n\r\n```typescript\r\n// app/api/ai/route.ts\r\nimport type { NextRequest } from 'next/server';\r\n\r\nexport async function POST(request: NextRequest) {\r\n const env = process.env as any;\r\n const { prompt } = await request.json();\r\n\r\n const response = await env.AI.run('@cf/meta/llama-3-8b-instruct', {\r\n prompt\r\n });\r\n\r\n return Response.json(response);\r\n}\r\n```\r\n\r\n**Wrangler config**:\r\n```jsonc\r\n{\r\n \"ai\": {\r\n \"binding\": \"AI\"\r\n }\r\n}\r\n```\r\n\r\n**See also**: `cloudflare-workers-ai` skill for complete AI patterns\r\n\r\n---\r\n\r\n### KV Storage (Key-Value)\r\n\r\n```typescript\r\n// app/api/cache/route.ts\r\nimport type { NextRequest } from 'next/server';\r\n\r\nexport async function GET(request: NextRequest) {\r\n const env = process.env as any;\r\n const { searchParams } = new URL(request.url);\r\n const key = searchParams.get('key');\r\n\r\n const value = await env.KV.get(key);\r\n return Response.json({ key, value });\r\n}\r\n\r\nexport async function PUT(request: NextRequest) {\r\n const env = process.env as any;\r\n const { key, value, ttl } = await request.json();\r\n\r\n await env.KV.put(key, value, { expirationTtl: ttl });\r\n return Response.json({ success: true });\r\n}\r\n```\r\n\r\n**See also**: `cloudflare-kv` skill for complete KV patterns",
"Use This Skill When": "- Deploying Next.js applications (App Router or Pages Router) to Cloudflare Workers\r\n- Need server-side rendering (SSR), static site generation (SSG), or incremental static regeneration (ISR) on Cloudflare\r\n- Migrating existing Next.js apps from Vercel, AWS, or other platforms to Cloudflare\r\n- Building full-stack Next.js applications with Cloudflare services (D1, R2, KV, Workers AI)\r\n- Need React Server Components, Server Actions, or Next.js middleware on Workers\r\n- Want global edge deployment with Cloudflare's network",
"Quick Reference": "npm run cf-typegen # Generate binding types\r\n```\r\n\r\n### Critical Configuration\r\n\r\n```jsonc\r\n// wrangler.jsonc\r\n{\r\n \"compatibility_date\": \"2025-05-05\", // Minimum!\r\n \"compatibility_flags\": [\"nodejs_compat\"] // Required!\r\n}\r\n```\r\n\r\n### Common Pitfalls\r\n\r\n1. ❌ Using Edge runtime → ✅ Use Node.js runtime\r\n2. ❌ Global DB clients → ✅ Request-scoped clients\r\n3. ❌ Old compatibility_date → ✅ Use 2025-05-05+\r\n4. ❌ Missing nodejs_compat → ✅ Add to compatibility_flags\r\n5. ❌ Only testing in `dev` → ✅ Always test `preview` before deploy\r\n6. ❌ Using Turbopack → ✅ Use standard Next.js build\r\n\r\n---\r\n\r\n**Production Tested**: Official Cloudflare support and active community\r\n**Token Savings**: ~59% vs manual setup\r\n**Errors Prevented**: 10+ documented issues\r\n**Last Verified**: 2025-10-21",
"Caching Configuration": "Configure caching behavior in `open-next.config.ts`:\r\n\r\n```typescript\r\nimport { defineCloudflareConfig } from \"@opennextjs/cloudflare\";\r\n\r\nexport default defineCloudflareConfig({\r\n // Custom cache configuration\r\n cache: {\r\n // Override default cache behavior\r\n // See: https://opennext.js.org/cloudflare/caching\r\n }\r\n});\r\n```\r\n\r\n**Default behavior**: OpenNext provides sensible caching defaults\r\n\r\n**Advanced usage**: See official OpenNext caching documentation"
}
}---
name: cloudflare-nextjs
description: |
Deploy Next.js applications (App Router and Pages Router) to Cloudflare Workers using the OpenNext adapter. This skill should be used when deploying Next.js apps with SSR, ISR, or server components to Cloudflare's serverless platform. It covers setup for both new and existing projects, configuration requirements, development workflows, integration with Cloudflare services (D1, R2, KV, Workers AI), and prevention of 10+ documented errors including worker size limits, runtime compatibility, database connection scoping, and security vulnerabilities.
Keywords: Cloudflare Next.js, OpenNext Cloudflare, @opennextjs/cloudflare, Next.js Workers, Next.js App Router Cloudflare, Next.js Pages Router Cloudflare, Next.js SSR Cloudflare, Next.js ISR, server components cloudflare, server actions cloudflare, Next.js middleware workers, nextjs d1, nextjs r2, nextjs kv, Next.js deployment, opennextjs-cloudflare cli, nodejs_compat, worker size limit, next.js runtime compatibility, database connection scoping, Next.js migration cloudflare
license: MIT
metadata:
version: 1.0.0
last_verified: 2025-10-21
package_versions:
"@opennextjs/cloudflare": "^1.3.0"
"next": "^14.2.0 || ^15.0.0"
"wrangler": "latest"
compatibility_requirements:
compatibility_date: "2025-05-05"
compatibility_flags: ["nodejs_compat"]
token_savings: "~59%"
errors_prevented: 10
official_docs: "https://opennext.js.org/cloudflare"
cloudflare_guide: "https://developers.cloudflare.com/workers/framework-guides/web-apps/nextjs/"
---
# Cloudflare Next.js Deployment Skill
Deploy Next.js applications to Cloudflare Workers using the OpenNext Cloudflare adapter for production-ready serverless Next.js hosting.
## Use This Skill When
- Deploying Next.js applications (App Router or Pages Router) to Cloudflare Workers
- Need server-side rendering (SSR), static site generation (SSG), or incremental static regeneration (ISR) on Cloudflare
- Migrating existing Next.js apps from Vercel, AWS, or other platforms to Cloudflare
- Building full-stack Next.js applications with Cloudflare services (D1, R2, KV, Workers AI)
- Need React Server Components, Server Actions, or Next.js middleware on Workers
- Want global edge deployment with Cloudflare's network
## Key Concepts
### OpenNext Adapter Architecture
The **OpenNext Cloudflare adapter** (`@opennextjs/cloudflare`) transforms Next.js build output into Cloudflare Worker-compatible format. This is fundamentally different from standard Next.js deployments:
- **Node.js Runtime Required**: Uses Node.js runtime in Workers (NOT Edge runtime)
- **Dual Development Workflow**: Test in both Next.js dev server AND workerd runtime
- **Custom Build Pipeline**: `next build` → OpenNext transformation → Worker deployment
- **Cloudflare-Specific Configuration**: Requires wrangler.jsonc and open-next.config.ts
### Critical Differences from Standard Next.js
| Aspect | Standard Next.js | Cloudflare Workers |
|--------|------------------|-------------------|
| Runtime | Node.js or Edge | Node.js (via nodejs_compat) |
| Dev Server | `next dev` | `next dev` + `opennextjs-cloudflare preview` |
| Deployment | Platform-specific | `opennextjs-cloudflare deploy` |
| Worker Size | No limit | 3 MiB (free) / 10 MiB (paid) |
| Database Connections | Global clients OK | Must be request-scoped |
| Image Optimization | Built-in | Via Cloudflare Images |
| Caching | Next.js cache | OpenNext config + Workers cache |
## Setup Patterns
### New Project Setup
Use Cloudflare's `create-cloudflare` (C3) CLI to scaffold a new Next.js project pre-configured for Workers:
```bash
npm create cloudflare@latest -- my-next-app --framework=next
```
**What this does**:
1. Runs Next.js official setup tool (`create-next-app`)
2. Installs `@opennextjs/cloudflare` adapter
3. Creates `wrangler.jsonc` with correct configuration
4. Creates `open-next.config.ts` for caching configuration
5. Adds deployment scripts to `package.json`
6. Optionally deploys immediately to Cloudflare
**Development workflow**:
```bash
npm run dev # Next.js dev server (fast reloads)
npm run preview # Test in workerd runtime (production-like)
npm run deploy # Build and deploy to Cloudflare
```
### Existing Project Migration
To add the OpenNext adapter to an existing Next.js application:
#### 1. Install the adapter
```bash
npm install --save-dev @opennextjs/cloudflare
```
#### 2. Create wrangler.jsonc
```jsonc
{
"name": "my-next-app",
"compatibility_date": "2025-05-05",
"compatibility_flags": ["nodejs_compat"]
}
```
**Critical configuration**:
- `compatibility_date`: **Minimum `2025-05-05`** (for FinalizationRegistry support)
- `compatibility_flags`: **Must include `nodejs_compat`** (for Node.js runtime)
#### 3. Create open-next.config.ts
```typescript
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
export default defineCloudflareConfig({
// Caching configuration (optional)
// See: https://opennext.js.org/cloudflare/caching
});
```
#### 4. Update package.json scripts
```json
{
"scripts": {
"dev": "next dev",
"build": "next build",
"preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview",
"deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy",
"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts"
}
}
```
**Script purposes**:
- `dev`: Next.js development server (fast iteration)
- `preview`: Build + run in workerd runtime (test before deploy)
- `deploy`: Build + deploy to Cloudflare
- `cf-typegen`: Generate TypeScript types for Cloudflare bindings
#### 5. Ensure Node.js runtime (not Edge)
Remove Edge runtime exports from your app:
```typescript
// ❌ REMOVE THIS (Edge runtime not supported)
export const runtime = "edge";
// ✅ Use Node.js runtime (default)
// No export needed - Node.js is default
```
## Development Workflow
### Dual Testing Strategy
**Always test in BOTH environments**:
1. **Next.js Dev Server** (`npm run dev`)
- Fast hot reloading
- Best developer experience
- Runs in Node.js (not production runtime)
- Use for rapid iteration
2. **Workerd Runtime** (`npm run preview`)
- Runs in production-like environment
- Catches runtime-specific issues
- Slower rebuild times
- **Required before deployment**
### When to Use Each
```bash
# Iterating on UI/logic → Use Next.js dev server
npm run dev
# Testing integrations (D1, R2, KV) → Use preview
npm run preview
# Before deploying → ALWAYS test preview
npm run preview
# Deploy to production
npm run deploy
```
## Configuration Requirements
### Wrangler Configuration
**Minimum requirements** in `wrangler.jsonc`:
```jsonc
{
"name": "your-app-name",
"compatibility_date": "2025-05-05", // Minimum for FinalizationRegistry
"compatibility_flags": ["nodejs_compat"] // Required for Node.js runtime
}
```
### Environment Variables for Package Exports
If using npm packages with multiple export conditions, create `.env`:
```env
WRANGLER_BUILD_CONDITIONS=""
WRANGLER_BUILD_PLATFORM="node"
```
This ensures Wrangler prioritizes the `node` export when available.
### Cloudflare Bindings Integration
Add bindings in `wrangler.jsonc`:
```jsonc
{
"name": "your-app-name",
"compatibility_date": "2025-05-05",
"compatibility_flags": ["nodejs_compat"],
// D1 Database
"d1_databases": [
{
"binding": "DB",
"database_name": "production-db",
"database_id": "your-database-id"
}
],
// R2 Storage
"r2_buckets": [
{
"binding": "BUCKET",
"bucket_name": "your-bucket"
}
],
// KV Storage
"kv_namespaces": [
{
"binding": "KV",
"id": "your-kv-id"
}
],
// Workers AI
"ai": {
"binding": "AI"
}
}
```
Access bindings in Next.js via `process.env`:
```typescript
// app/api/route.ts
import type { NextRequest } from 'next/server';
export async function GET(request: NextRequest) {
// Access Cloudflare bindings
const env = process.env as any;
// D1 Database query
const result = await env.DB.prepare('SELECT * FROM users').all();
// R2 Storage access
const file = await env.BUCKET.get('file.txt');
// KV Storage access
const value = await env.KV.get('key');
// Workers AI inference
const response = await env.AI.run('@cf/meta/llama-3-8b-instruct', {
prompt: 'Hello AI'
});
return Response.json({ result });
}
```
## Error Prevention (10+ Documented Errors)
### 1. Worker Size Limit Exceeded (3 MiB - Free Plan)
**Error**: `"Your Worker exceeded the size limit of 3 MiB"`
**Cause**: Workers Free plan limits Worker size to 3 MiB (gzip-compressed)
**Solutions**:
- Upgrade to Workers Paid plan (10 MiB limit)
- Analyze bundle size and remove unused dependencies
- Use dynamic imports to code-split large dependencies
**Bundle analysis**:
```bash
npx opennextjs-cloudflare build
cd .open-next/server-functions/default
# Analyze handler.mjs.meta.json with ESBuild Bundle Analyzer
```
**Source**: https://opennext.js.org/cloudflare/troubleshooting#worker-size-limits
---
### 2. Worker Size Limit Exceeded (10 MiB - Paid Plan)
**Error**: `"Your Worker exceeded the size limit of 10 MiB"`
**Cause**: Unnecessary code bundled into Worker
**Debug workflow**:
1. Run `npx opennextjs-cloudflare build`
2. Navigate to `.open-next/server-functions/default`
3. Analyze `handler.mjs.meta.json` using ESBuild Bundle Analyzer
4. Identify and remove/externalize large dependencies
**Source**: https://opennext.js.org/cloudflare/troubleshooting#worker-size-limits
---
### 3. FinalizationRegistry Not Defined
**Error**: `"ReferenceError: FinalizationRegistry is not defined"`
**Cause**: `compatibility_date` in wrangler.jsonc is too old
**Solution**: Update `compatibility_date` to `2025-05-05` or later:
```jsonc
{
"compatibility_date": "2025-05-05" // Minimum for FinalizationRegistry
}
```
**Source**: https://opennext.js.org/cloudflare/troubleshooting#finalizationregistry-is-not-defined
---
### 4. Cannot Perform I/O on Behalf of Different Request
**Error**: `"Cannot perform I/O on behalf of a different request"`
**Cause**: Database client created globally and reused across requests
**Problem code**:
```typescript
// ❌ WRONG: Global DB client
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
export async function GET() {
// This will fail - pool created in different request context
const result = await pool.query('SELECT * FROM users');
return Response.json(result);
}
```
**Solution**: Create database clients inside request handlers:
```typescript
// ✅ CORRECT: Request-scoped DB client
import { Pool } from 'pg';
export async function GET() {
// Create client within request context
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const result = await pool.query('SELECT * FROM users');
await pool.end();
return Response.json(result);
}
```
**Alternative**: Use Cloudflare D1 (designed for Workers) instead of external databases:
```typescript
// ✅ BEST: Use D1 (no connection pooling needed)
export async function GET(request: NextRequest) {
const env = process.env as any;
const result = await env.DB.prepare('SELECT * FROM users').all();
return Response.json(result);
}
```
**Source**: https://opennext.js.org/cloudflare/troubleshooting#cannot-perform-io-on-behalf-of-a-different-request
---
### 5. NPM Package Import Failures
**Error**: `"Could not resolve '<package>'"`
**Cause**: Missing `nodejs_compat` flag or package export conditions
**Solution 1**: Enable `nodejs_compat` flag:
```jsonc
{
"compatibility_flags": ["nodejs_compat"]
}
```
**Solution 2**: For packages with multiple exports, create `.env`:
```env
WRANGLER_BUILD_CONDITIONS=""
WRANGLER_BUILD_PLATFORM="node"
```
**Source**: https://opennext.js.org/cloudflare/troubleshooting#npm-packages-fail-to-import
---
### 6. Failed to Load Chunk (Turbopack)
**Error**: `"Failed to load chunk server/chunks/ssr/"`
**Cause**: Next.js built with Turbopack (`next build --turbo`)
**Solution**: Use standard build (Turbopack not supported by adapter):
```json
{
"scripts": {
"build": "next build" // ✅ Correct
// "build": "next build --turbo" // ❌ Don't use Turbopack
}
}
```
**Source**: https://opennext.js.org/cloudflare/troubleshooting#failed-to-load-chunk
---
### 7. SSRF Vulnerability (CVE-2025-6087)
**Vulnerability**: Server-Side Request Forgery via `/_next/image` endpoint
**Affected versions**: `@opennextjs/cloudflare` < 1.3.0
**Solution**: Upgrade to version 1.3.0 or later:
```bash
npm install --save-dev @opennextjs/cloudflare@^1.3.0
```
**Impact**: Allows unauthenticated users to proxy arbitrary remote content
**Source**: https://github.com/advisories/GHSA-rvpw-p7vw-wj3m
---
### 8. Durable Objects Binding Warnings
**Warning**: `"You have defined bindings to the following internal Durable Objects... will not work in local development, but they should work in production"`
**Cause**: OpenNext uses Durable Objects for caching (`DOQueueHandler`, `DOShardedTagCache`)
**Solution**: **Safe to ignore** - warning is expected behavior
**Alternative** (to suppress warning): Define Durable Objects in separate Worker with own config
**Source**: https://opennext.js.org/cloudflare/known-issues#caching-durable-objects
---
### 9. Prisma + D1 Middleware Conflicts
**Error**: Build errors when using `@prisma/client` + `@prisma/adapter-d1` in Next.js middleware
**Cause**: Database initialization in middleware context
**Workaround**: Initialize Prisma client in route handlers, not middleware
**Source**: https://github.com/opennextjs/opennextjs-cloudflare/issues/471
---
### 10. cross-fetch Library Errors
**Error**: Errors when using libraries that depend on `cross-fetch`
**Cause**: OpenNext patches deployment package causing `cross-fetch` to try using Node.js libraries when native fetch is available
**Solution**: Use native `fetch` API directly instead of `cross-fetch`:
```typescript
// ✅ Use native fetch
const response = await fetch('https://api.example.com/data');
// ❌ Avoid cross-fetch
// import fetch from 'cross-fetch';
```
**Source**: https://opennext.js.org/cloudflare/troubleshooting
---
### 11. Windows Development Issues
**Issue**: Full Windows support not guaranteed
**Cause**: Underlying Next.js tooling issues on Windows
**Solutions**:
- Use WSL (Windows Subsystem for Linux)
- Use virtual machine with Linux
- Use Linux-based CI/CD for deployments
**Source**: https://opennext.js.org/cloudflare#windows-support
## Feature Support Matrix
| Feature | Status | Notes |
|---------|--------|-------|
| **App Router** | ✅ Fully Supported | Latest App Router features work |
| **Pages Router** | ✅ Fully Supported | Legacy Pages Router supported |
| **Route Handlers** | ✅ Fully Supported | API routes work as expected |
| **React Server Components** | ✅ Fully Supported | RSC fully functional |
| **Server Actions** | ✅ Fully Supported | Server Actions work |
| **SSG** | ✅ Fully Supported | Static Site Generation |
| **SSR** | ✅ Fully Supported | Server-Side Rendering |
| **ISR** | ✅ Fully Supported | Incremental Static Regeneration |
| **Middleware** | ✅ Supported | Except Node.js middleware (15.2+) |
| **Image Optimization** | ✅ Supported | Via Cloudflare Images |
| **Partial Prerendering (PPR)** | ✅ Supported | Experimental in Next.js |
| **Composable Caching** | ✅ Supported | `'use cache'` directive |
| **Response Streaming** | ✅ Supported | Streaming responses work |
| **`next/after` API** | ✅ Supported | Post-response async work |
| **Node.js Middleware (15.2+)** | ❌ Not Supported | Future support planned |
| **Edge Runtime** | ❌ Not Supported | Use Node.js runtime |
**Source**: https://developers.cloudflare.com/workers/framework-guides/web-apps/nextjs/#next-js-supported-features
## Integration with Cloudflare Services
### D1 Database (SQL)
```typescript
// app/api/users/route.ts
import type { NextRequest } from 'next/server';
export async function GET(request: NextRequest) {
const env = process.env as any;
const result = await env.DB.prepare(
'SELECT * FROM users WHERE active = ?'
).bind(true).all();
return Response.json(result.results);
}
export async function POST(request: NextRequest) {
const env = process.env as any;
const { name, email } = await request.json();
const result = await env.DB.prepare(
'INSERT INTO users (name, email) VALUES (?, ?)'
).bind(name, email).run();
return Response.json({ id: result.meta.last_row_id });
}
```
**Wrangler config**:
```jsonc
{
"d1_databases": [
{
"binding": "DB",
"database_name": "production-db",
"database_id": "your-database-id"
}
]
}
```
**See also**: `cloudflare-d1` skill for complete D1 patterns
---
### R2 Storage (Object Storage)
```typescript
// app/api/upload/route.ts
import type { NextRequest } from 'next/server';
export async function POST(request: NextRequest) {
const env = process.env as any;
const formData = await request.formData();
const file = formData.get('file') as File;
// Upload to R2
await env.BUCKET.put(file.name, file.stream(), {
httpMetadata: {
contentType: file.type
}
});
return Response.json({ success: true, filename: file.name });
}
export async function GET(request: NextRequest) {
const env = process.env as any;
const { searchParams } = new URL(request.url);
const filename = searchParams.get('file');
const object = await env.BUCKET.get(filename);
if (!object) {
return new Response('Not found', { status: 404 });
}
return new Response(object.body, {
headers: {
'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream'
}
});
}
```
**See also**: `cloudflare-r2` skill for complete R2 patterns
---
### Workers AI (Model Inference)
```typescript
// app/api/ai/route.ts
import type { NextRequest } from 'next/server';
export async function POST(request: NextRequest) {
const env = process.env as any;
const { prompt } = await request.json();
const response = await env.AI.run('@cf/meta/llama-3-8b-instruct', {
prompt
});
return Response.json(response);
}
```
**Wrangler config**:
```jsonc
{
"ai": {
"binding": "AI"
}
}
```
**See also**: `cloudflare-workers-ai` skill for complete AI patterns
---
### KV Storage (Key-Value)
```typescript
// app/api/cache/route.ts
import type { NextRequest } from 'next/server';
export async function GET(request: NextRequest) {
const env = process.env as any;
const { searchParams } = new URL(request.url);
const key = searchParams.get('key');
const value = await env.KV.get(key);
return Response.json({ key, value });
}
export async function PUT(request: NextRequest) {
const env = process.env as any;
const { key, value, ttl } = await request.json();
await env.KV.put(key, value, { expirationTtl: ttl });
return Response.json({ success: true });
}
```
**See also**: `cloudflare-kv` skill for complete KV patterns
## Image Optimization
Next.js image optimization works via Cloudflare Images. Configure in `open-next.config.ts`:
```typescript
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
export default defineCloudflareConfig({
imageOptimization: {
loader: 'cloudflare'
}
});
```
Usage in components:
```tsx
import Image from 'next/image';
export default function Avatar() {
return (
<Image
src="/avatar.jpg"
alt="User avatar"
width={200}
height={200}
// Automatically optimized via Cloudflare Images
/>
);
}
```
**Billing**: Cloudflare Images usage is billed separately
**Docs**: https://developers.cloudflare.com/images/
## Caching Configuration
Configure caching behavior in `open-next.config.ts`:
```typescript
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
export default defineCloudflareConfig({
// Custom cache configuration
cache: {
// Override default cache behavior
// See: https://opennext.js.org/cloudflare/caching
}
});
```
**Default behavior**: OpenNext provides sensible caching defaults
**Advanced usage**: See official OpenNext caching documentation
## Known Limitations
### Not Yet Supported
1. **Node.js Middleware (Next.js 15.2+)**
- Introduced in Next.js 15.2
- Support planned for future releases
- Use standard middleware for now
2. **Edge Runtime**
- Only Node.js runtime supported
- Remove `export const runtime = "edge"` from your app
3. **Full Windows Support**
- Development on Windows not fully guaranteed
- Use WSL, VM, or Linux-based CI/CD
### Worker Size Constraints
- **Free plan**: 3 MiB limit (gzip-compressed)
- **Paid plan**: 10 MiB limit (gzip-compressed)
- Monitor bundle size during development
- Use dynamic imports for code splitting
### Database Connections
- External database clients (PostgreSQL, MySQL) must be request-scoped
- Cannot reuse connections across requests (Workers limitation)
- Prefer Cloudflare D1 for database needs (designed for Workers)
## Deployment
### Deploy from Local Machine
```bash
# Build and deploy in one command
npm run deploy
# Or step by step:
npx opennextjs-cloudflare build
npx opennextjs-cloudflare deploy
```
### Deploy from CI/CD
Configure deployment command in your CI/CD system:
```bash
npm run deploy
```
**Examples**:
- GitHub Actions: `.github/workflows/deploy.yml`
- GitLab CI: `.gitlab-ci.yml`
- Cloudflare Workers Builds: Auto-detects `npm run deploy`
**Environment variables**: Set secrets in Cloudflare dashboard or CI/CD system
### Custom Domains
Add custom domain in Cloudflare dashboard:
1. Navigate to Workers & Pages
2. Select your Worker
3. Settings → Domains & Routes
4. Add custom domain
**DNS**: Domain must be on Cloudflare (zone required)
## TypeScript Support
Generate types for Cloudflare bindings:
```bash
npm run cf-typegen
```
Creates `cloudflare-env.d.ts` with types for your bindings:
```typescript
// cloudflare-env.d.ts (auto-generated)
interface CloudflareEnv {
DB: D1Database;
BUCKET: R2Bucket;
KV: KVNamespace;
AI: Ai;
}
```
Use in route handlers:
```typescript
import type { NextRequest } from 'next/server';
export async function GET(request: NextRequest) {
const env = process.env as CloudflareEnv;
// Now env.DB, env.BUCKET, etc. are typed
}
```
## Testing
### Local Testing (Development)
```bash
# Next.js dev server (fast iteration)
npm run dev
```
### Local Testing (Production-like)
```bash
# Workerd runtime (catches Workers-specific issues)
npm run preview
```
### Integration Testing
Always test in `preview` mode before deploying:
```bash
# Build and run in workerd
npm run preview
# Test bindings (D1, R2, KV, AI)
# Test middleware
# Test API routes
# Test SSR/ISR behavior
```
## Migration from Other Platforms
### From Vercel
1. Copy existing Next.js project
2. Run existing project migration steps (above)
3. Update environment variables in Cloudflare dashboard
4. Replace Vercel-specific features:
- Vercel Postgres → Cloudflare D1
- Vercel Blob → Cloudflare R2
- Vercel KV → Cloudflare KV
- Vercel Edge Config → Cloudflare KV
5. Test thoroughly with `npm run preview`
6. Deploy with `npm run deploy`
### From AWS / Other Platforms
Same process as Vercel migration - the adapter handles Next.js standard features automatically.
## Resources
### Official Documentation
- **OpenNext Cloudflare**: https://opennext.js.org/cloudflare
- **Cloudflare Next.js Guide**: https://developers.cloudflare.com/workers/framework-guides/web-apps/nextjs/
- **Next.js Docs**: https://nextjs.org/docs
### Troubleshooting
- **Troubleshooting Guide**: https://opennext.js.org/cloudflare/troubleshooting
- **Known Issues**: https://opennext.js.org/cloudflare/known-issues
- **GitHub Issues**: https://github.com/opennextjs/opennextjs-cloudflare/issues
### Related Skills
- `cloudflare-worker-base` - Base Worker setup with Hono + Vite + React
- `cloudflare-d1` - D1 database integration
- `cloudflare-r2` - R2 object storage
- `cloudflare-kv` - KV key-value storage
- `cloudflare-workers-ai` - Workers AI integration
- `cloudflare-vectorize` - Vector database for RAG
## Quick Reference
### Essential Commands
```bash
# New project
npm create cloudflare@latest -- my-next-app --framework=next
# Development
npm run dev # Fast iteration (Next.js dev server)
npm run preview # Test in workerd (production-like)
# Deployment
npm run deploy # Build and deploy to Cloudflare
# TypeScript
npm run cf-typegen # Generate binding types
```
### Critical Configuration
```jsonc
// wrangler.jsonc
{
"compatibility_date": "2025-05-05", // Minimum!
"compatibility_flags": ["nodejs_compat"] // Required!
}
```
### Common Pitfalls
1. ❌ Using Edge runtime → ✅ Use Node.js runtime
2. ❌ Global DB clients → ✅ Request-scoped clients
3. ❌ Old compatibility_date → ✅ Use 2025-05-05+
4. ❌ Missing nodejs_compat → ✅ Add to compatibility_flags
5. ❌ Only testing in `dev` → ✅ Always test `preview` before deploy
6. ❌ Using Turbopack → ✅ Use standard Next.js build
---
**Production Tested**: Official Cloudflare support and active community
**Token Savings**: ~59% vs manual setup
**Errors Prevented**: 10+ documented issues
**Last Verified**: 2025-10-21