ZoyaPatel
Ahmedabad

Sample Post Final

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.

how-to-deploy-nextjs-app-on-cloudflare-workers-custom-domain

Requirements! To follow this guide, you will need: 
  • 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`.

  1. Step 1: Create or Prepare Your Next.js Project
  2. You need a Next.js project. You can create a new one or use an existing one. I recommend starting fresh for learning.

    • 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.
  3. Step 2: Install Dependencies for Existing Project
  4. If you skipped the automatic setup, install the required packages manually. You need the OpenNext adapter and Cloudflare's Wrangler CLI.

    • Install Adapter: Run `npm install --save-dev @opennextjs/cloudflare`.
    • Install Wrangler: Run `npm install --save-dev wrangler`. This tool deploys your Worker.
  5. Step 3: Create Configuration Files
  6. You need two configuration files. One for the adapter and one for Wrangler. Place them in your project root.

    • 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:
    {
    

    "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"

    }

    }

  • Step 4: Update package.json Scripts
  • 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.
    Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.

    {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 Email
    John Doe john@example.com
    Mary Moe mary@example.com
    July Dooley july@example.com


    Contact Form

    {contactForm}

    Error 1: Turbopack Build Failure! Next.js 16+ uses Turbopack by default. The OpenNext adapter currently does not support Turbopack. Solution: Force webpack by running `next build --webpack` or set `"turbo": false` in your `next.config.js`.
    Error 2: Worker Exceeds 3 MiB Limit! The free tier limits Workers to 3 MiB compressed. Run `npm run deploy` and check the gzipped size. If it exceeds 3 MiB, upgrade to a Workers Paid plan ($5/month) for 10 MiB limit.
    Troubleshooting 1: Local Preview Works but Deploy Fails. Ensure your `compatibility_date` is not in the future. Cloudflare rejects future dates. Update it to a valid past date like "2025-05-05". Also check that `nodejs_compat` is in your flags.

    Deploying to Cloudflare Workers

    Now we deploy the configured application. You will connect to your Cloudflare account. The command line handles the authentication.

    1. Step 5: Authenticate Wrangler
    2. You need to log in to Cloudflare from your terminal. This gives Wrangler permission to deploy.

      • 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.
    3. Step 6: Build the OpenNext Bundle
    4. Transform your Next.js application for the Workers runtime. This step creates the `.open-next` folder.

      • 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.
    5. Step 7: Deploy to Workers
    6. Upload your Worker to Cloudflare's network. The command will output a live `*.workers.dev` URL.

      • 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.

    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.

    1. Step 8: Add Custom Domain via Dashboard (Recommended)
    2. The dashboard method is the simplest. It does not require modifying DNS records manually. Cloudflare handles everything.

      • 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.
    3. Step 9: Verify DNS Propagation
    4. DNS changes take a few minutes to propagate. You can check the status in the dashboard.

      • 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.

    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.

    Error 3: Missing nodejs_compat Flag! Your Worker fails with "ReferenceError: require is not defined". Solution: Add `"compatibility_flags": ["nodejs_compat"]` to your `wrangler.jsonc` file and redeploy.
    Error 4: Middleware Errors with Next.js 15+! The Node.js runtime in middleware is unsupported. Change your middleware to use Edge Runtime. Add `export const runtime = 'edge'` to your `middleware.ts` file.
    Error 5: Database Connection I/O Errors! Workers cannot reuse database connections across requests. Use connection pooling or a serverless database adapter (e.g., Neon, Supabase). Instantiate the database client inside each request handler.
    Troubleshooting 2: Environment Variables Not Working. Create a `.dev.vars` file for local development. For production, set them in the Cloudflare dashboard under your Worker's **Settings > Variables**. Use `wrangler secret put MY_SECRET` for secrets.
    Troubleshooting 3: Static Assets (Images/CSS) Missing. Ensure your `wrangler.jsonc` contains the `assets` binding pointing to `.open-next/assets`. Run `opennextjs-cloudflare build` to refresh the assets folder.

    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

    {fullWidth}
    Mumbai
    Kolkata

    Thank you for taking time to comment. 😊 💙

    🔔 If you're interested in more, have questions, or any constructive criticism, let us know in the comment section below!

    ⚠ Your email address will not be published. By using this form you agree with the storage and handling of your data by this website.

    Bangalore
    Previous Post Next Post