Short Summary: Deploy a Next.js app to Cloudflare Workers with a custom domain by running npm create cloudflare@latest to scaffold a new project, or manually install @opennextjs/cloudflare, configure wrangler.jsonc with nodejs_compat and a compatibility_date of 2025-05-05 or later, deploy via npm run deploy, then add your custom domain in the Cloudflare dashboard under Workers & Pages > your Worker > Settings > Domains & Routes > Custom Domain.
I have deployed dozens of Next.js applications to various platforms like Vercel and Netlify. I have a problem with high cold start latency and unpredictable costs when scaling globally. I needed a low-latency, cost-effective solution that could handle SSR, ISR, and middleware without modification. I discovered Cloudflare Workers paired with OpenNext solves this perfectly.
You might face the same issues. Cloudflare Workers offers near-zero cold starts and 300+ global edge locations. The free tier provides millions of requests per day. The challenge is the configuration. The OpenNext adapter transforms Next.js for the Worker runtime. You need a clear guide to connect your custom domain. This article provides that guide. I wrote this for developers like you.
Deploy Next.js App on Cloudflare Workers with Custom Domain
We will cover prerequisites, installation, configuration, and deployment. You will learn to attach a custom domain to your Worker. You will also learn to troubleshoot common errors. This guide is for Next.js 14 and 15. You can use the App Router or Pages Router. The outcome is a fully functional Next.js app on your own domain. It will run at the edge with minimal latency.
- Node.js 18.x or higher (20.x recommended) installed on your machine.
- A Cloudflare account (free tier works) with a verified email address.
- A registered domain name (e.g., example.com) managed by Cloudflare or accessible via DNS update.
- Basic knowledge of Next.js and the command line.
🚀 Sign Up for Cloudflare (Free)
How OpenNext Works on Cloudflare Workers
Understanding the technology helps avoid configuration mistakes. Next.js normally runs on Node.js. Cloudflare Workers run on the V8 engine. They use the workerd runtime. OpenNext bridges this gap.
The OpenNext adapter transforms the Next.js build output. It makes it compatible with the Worker environment. It preserves features like SSR, ISR, and API routes. It does this without major code changes. The adapter uses the Node.js compatibility layer. This gives you access to most Node.js APIs.
| Next.js Feature | Cloudflare OpenNext Support |
|---|---|
| App Router | ✅ Supported |
| Server-Side Rendering (SSR) | ✅ Supported |
| Incremental Static Regeneration (ISR) | ✅ Supported |
| Middleware | ✅ Supported |
| Image Optimization | ⚠️ Via Cloudflare Images binding |
| Node.js Runtime in Middleware | ❌ Not yet supported |
Most features work out of the box. The Node.js runtime in middleware is an exception. Use Edge Runtime for middleware functions. The image optimization requires a Cloudflare Images binding. Otherwise, disable it with unoptimized: true.
Tip 1: Compatibility Date
Always set your `compatibility_date` to the latest stable version. Use `2025-05-05` or newer. This ensures access to critical Node.js polyfills like `FinalizationRegistry`.
- Step 1: Create or Prepare Your Next.js Project
- New Project (Easiest): Run `npm create cloudflare@latest my-next-app --framework=next --platform=workers`. This scaffolds a configured project instantly.
- Existing Project: Navigate to your project folder. Run `wrangler setup` for automatic detection and configuration. Or follow manual steps below.
- Step 2: Install Dependencies for Existing Project
- Install Adapter: Run `npm install --save-dev @opennextjs/cloudflare`.
- Install Wrangler: Run `npm install --save-dev wrangler`. This tool deploys your Worker.
- Step 3: Create Configuration Files
- open-next.config.ts: Create this file. Export a default config: `import { defineCloudflareConfig } from "@opennextjs/cloudflare"; export default defineCloudflareConfig();`.
- wrangler.jsonc (or wrangler.toml): Create this file. Add the following JSON:
You need a Next.js project. You can create a new one or use an existing one. I recommend starting fresh for learning.
If you skipped the automatic setup, install the required packages manually. You need the OpenNext adapter and Cloudflare's Wrangler CLI.
You need two configuration files. One for the adapter and one for Wrangler. Place them in your project root.
{
"name": "my-next-app",
"main": ".open-next/worker.js",
"compatibility_date": "2025-05-05",
"compatibility_flags": ["nodejs_compat"],
"assets": {
"directory": ".open-next/assets",
"binding": "ASSETS"
}
}
Add helpful scripts to your `package.json`. This makes building and deploying easier.
- Build Script: Set `"build": "next build"`. This runs the standard Next.js build.
- Deploy Script: Add `"deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy || npx wrangler deploy"`. This builds the OpenNext bundle and deploys it.
- Preview Script: Add `"preview": "opennextjs-cloudflare build && wrangler dev"`. This runs a local preview.
{getToc} $title={Table of Contents}
Default Buttons
{getButton} $text={Default Button} {getButton} $text={Preview} $icon={preview} {getButton} $text={Download} $icon={download} {getButton} $text={Visit a Link} $icon={link} {getButton} $text={Buy Now} $icon={cart} {getButton} $text={Share} $icon={share} {getButton} $text={Contact Us} $icon={contact} {getButton} $text={More Info} $icon={info}Colored Buttons
{getButton} $text={Colored Button} $color={#10b981} {getButton} $text={Preview} $icon={preview} $color={#22c55e} {getButton} $text={Download} $icon={download} $color={#3b82f6} {getButton} $text={Visit a Link} $icon={link} $color={#a855f7} {getButton} $text={Buy Now} $icon={cart} $color={#f97316} {getButton} $text={Share} $icon={share} $color={#ef4444} {getButton} $text={Contact Us} $icon={contact} $color={#eab308} {getButton} $text={More Info} $icon={info} $color={#334155}Download Button
{getButton} $text={Download} $icon={download} $info={64kb}Purchase Button
{getButton} $text={Buy Now} $icon={cart} $info={$19}Alert Boxes
Lorem Ipsum is not simply random text. It has roots in a piece of classical test link here too Latin literature from 45 BC, making it over 2000 years old. {alertSuccess}
Lorem Ipsum is not simply random text. It has roots in a piece of classical test link here too Latin literature from 45 BC, making it over 2000 years old. {alertInfo}
Lorem Ipsum is not simply random text. It has roots in a piece of classical test link here too Latin literature from 45 BC, making it over 2000 years old. {alertWarning}
Lorem Ipsum is not simply random text. It has roots in a piece of classical test link here too Latin literature from 45 BC, making it over 2000 years old. {alertError}
Bordered Table
| Firstname | Lastname | |
|---|---|---|
| John | Doe | john@example.com |
| Mary | Moe | mary@example.com |
| July | Dooley | july@example.com |
Contact Form
{contactForm}Deploying to Cloudflare Workers
Now we deploy the configured application. You will connect to your Cloudflare account. The command line handles the authentication.
- Step 5: Authenticate Wrangler
- Run Login: Type `npx wrangler login`. A browser window opens.
- Authorize: Select your Cloudflare account and click "Allow". This authenticates your CLI.
- Check Status: Run `npx wrangler whoami`. It should display your account email.
- Step 6: Build the OpenNext Bundle
- Run OpenNext Build: Execute `npx opennextjs-cloudflare build`. This runs `next build` and transforms the output.
- Check Output: Look for a folder named `.open-next` in your project root. It contains the bundled Worker code.
- Step 7: Deploy to Workers
- Deploy Command: Run `npx wrangler deploy`. This uploads the `.open-next` directory.
- Confirm Success: You will see a URL like `https://my-next-app.your-subdomain.workers.dev`. Open it to verify your app works.
You need to log in to Cloudflare from your terminal. This gives Wrangler permission to deploy.
Transform your Next.js application for the Workers runtime. This step creates the `.open-next` folder.
Upload your Worker to Cloudflare's network. The command will output a live `*.workers.dev` URL.
Next.js on Cloudflare
Tip 2: Use GitHub Actions for CI/CD
Automate deployments with GitHub Actions. Add secrets for `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN`. Then use `npm run deploy` in your workflow.
Attaching a Custom Domain
Using a *.workers.dev URL is fine for testing. Production apps need a custom domain. This section shows two methods.
- Step 8: Add Custom Domain via Dashboard (Recommended)
- Navigate: Log in to your Cloudflare dashboard. Go to **Workers & Pages**.
- Select Worker: Click on your deployed Worker's name (e.g., "my-next-app").
- Go to Domains: Click the **Domains** tab or go to **Settings > Domains & Routes**.
- Add Custom Domain: Click **Add** > **Custom Domain**. Enter your domain (e.g., `www.example.com` or `app.example.com`).
- Confirm: Click **Add Custom Domain**. Cloudflare creates DNS records automatically.
- Step 9: Verify DNS Propagation
- Dashboard Status: The custom domain will show "Active" when ready.
- Test Access: Visit your custom domain in a browser. You should see your Next.js app.
- Check SSL: Cloudflare issues a free SSL certificate automatically. Verify the padlock icon is visible.
The dashboard method is the simplest. It does not require modifying DNS records manually. Cloudflare handles everything.
DNS changes take a few minutes to propagate. You can check the status in the dashboard.
Tip 3: Root Domain (example.com) Handling
To use a naked domain, add it as a Custom Domain directly. Ensure your Cloudflare zone includes the root domain. Cloudflare's proxy will handle the A record automatically.
Note: The embedded video placeholder is for illustrative purposes. Replace with a relevant tutorial video ID.
Common Errors and How to Fix Them
Deployments rarely go perfectly on the first try. You might encounter these specific errors.
Best Practices for Production
Follow these optimization tips to keep your Next.js app fast and stable on Workers.
Best Practice 1: Enable R2 Caching for ISR.
For Incremental Static Regeneration (ISR), configure R2 bucket caching. This persists rendered pages across Worker restarts. Add caching overrides in `open-next.config.ts`.
Best Practice 2: Reduce Worker Bundle Size.
Analyze your bundle with `npx opennextjs-cloudflare build` and check `.open-next/server-functions/default/handler.mjs.meta.json`. Upload the JSON to esbuild's analyzer to find large dependencies.
Best Practice 3: Monitor with Cloudflare Analytics.
Use the Workers Metrics tab to track request duration and error rates. Set up alerts for high 5xx error rates to detect deployment issues early.
| Action | Tool/Method | Impact |
|---|---|---|
| Reduce Cold Starts | Keep Worker under 1 MiB compressed | Significant latency reduction |
| Optimize Images | Use Cloudflare Images or pre-optimized assets | Lower bandwidth costs |
| Secure Headers | Add middleware for CSP and CORS | Better security posture |
How To Connect Custom Domain To Cloudflare Pages
Can I use Next.js middleware with Cloudflare Workers?
Yes, but only with the Edge Runtime. Add `export const runtime = 'edge'` to your `middleware.ts`. The Node.js runtime for middleware is currently unsupported.
Does OpenNext support Next.js 16?
Yes, all minor and patch versions of Next.js 16 are supported. However, you must force Webpack builds instead of Turbopack using `next build --webpack`.
What happens if my Worker exceeds the 3 MiB limit?
The deployment will fail with an API error. You can either reduce bundle size by eliminating dependencies, or upgrade to a Workers Paid plan ($5/month) for a 10 MiB limit.
How do I use environment variables in production?
Add them as plain text or secrets in the Cloudflare dashboard. Go to Workers & Pages > your Worker > Settings > Variables. For secrets, use the "Add secret" button.
Is ISR (Incremental Static Regeneration) supported?
Yes, ISR is fully supported. However, you need to configure an R2 bucket for caching. This preserves regenerated pages across Worker restarts.
Why do I see "Cannot perform I/O on behalf of a different request"?
This occurs with database clients that reuse connections. Workers prohibit sharing I/O objects across requests. Use a serverless database adapter or instantiate the client per request.
How do I point a root domain (example.com) to my Worker?
Add the root domain as a Custom Domain in the dashboard. Cloudflare automatically creates the necessary A/AAAA records. No manual DNS changes are needed.
Sources:
https://developers.cloudflare.com/workers/framework-guides/web-apps/nextjs/
https://opennext.js.org/cloudflare
https://developers.cloudflare.com/workers/configuration/routing/custom-domains/
Tags: Next.js, Cloudflare Workers, OpenNext, Edge Computing