How to Solve CORS Errors While Fetching External APIs in Next.js: 4 Proven Methods

To solve CORS errors in Next.js, configure your application to return the required Access-Control-Allow-Origin header using either API route middleware, Edge middleware, next.config.js headers, or a server-side proxy that fetches external data on behalf of the browser.

Cross-Origin Resource Sharing (CORS) errors occur when a browser blocks a request to a different origin because the response lacks the mandatory CORS headers. In the vercel/next.js repository, the framework provides multiple layers for injecting these headers, ranging from per-route middleware to global configuration. Understanding where to add these headers—and how Next.js internally validates cross-site requests—ensures your external API calls succeed in production.

Understanding Next.js CORS Internals

Before implementing a fix, it helps to understand how Next.js handles cross-site requests internally. The server includes a security layer in packages/next/src/server/lib/router-utils/block-cross-site.ts that aborts requests early when they appear to be disallowed cross-site script tags with no-cors mode. For standard fetch requests, the router proceeds to your API routes or middleware, where you can inject the necessary headers.

The repository also includes integration tests in test/integration/api-support/pages/api/cors.js that assert CORS headers are correctly set, providing a reference for valid implementations.

Method 1: API Route Middleware with the CORS Package

The most common approach for adding CORS headers to specific endpoints uses Express-style middleware within individual API routes. The官方 example in examples/api-routes-cors/pages/api/cors.ts demonstrates this pattern using the cors npm package and a helper function called initMiddleware.

// examples/api-routes-cors/pages/api/cors.ts
import Cors from 'cors';
import type { NextApiRequest, NextApiResponse } from 'next';

function initMiddleware(middleware: any) {
  return (req: NextApiRequest, res: NextApiResponse) =>
    new Promise((resolve, reject) => {
      middleware(req, res, (result: any) => {
        if (result instanceof Error) return reject(result);
        return resolve(result);
      });
    });
}

const cors = initMiddleware(
  Cors({
    origin: '*',
    methods: ['GET', 'POST', 'OPTIONS'],
  })
);

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  await cors(req, res);

  if (req.method === 'OPTIONS') {
    res.status(200).end();
    return;
  }

  const apiRes = await fetch('https://api.example.com/data');
  const data = await apiRes.json();
  res.status(200).json(data);
}

This approach gives you granular control over which routes support cross-origin requests and allows dynamic origin validation.

Method 2: Global Edge Middleware

If your application requires CORS headers across many routes, configuring a global Edge middleware is more maintainable than repeating code in every API route. The middleware runs before requests reach your API routes or pages and can modify responses using NextResponse.next({ headers }).

// middleware.js
import { NextResponse } from 'next/server';

export function middleware(request) {
  const response = NextResponse.next();

  if (request.nextUrl.pathname.startsWith('/api/')) {
    response.headers.set('Access-Control-Allow-Origin', '*');
    response.headers.set('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
    response.headers.set('Access-Control-Allow-Headers', 'Content-Type,Authorization');
  }

  if (request.method === 'OPTIONS') {
    return new NextResponse(null, {
      status: 204,
      headers: response.headers,
    });
  }

  return response;
}

According to the Next.js edge middleware documentation, this approach runs at the edge runtime, providing low-latency header injection for serverless deployments.

Method 3: Static Headers in next.config.js

For scenarios where you need CORS headers on specific path patterns without writing runtime code, use the headers configuration in next.config.js. The examples/api-routes-cors/next.config.js file in the repository demonstrates matching /api/:path* to apply headers globally to all API routes.

// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/api/:path*',
        headers: [
          {
            key: 'Access-Control-Allow-Origin',
            value: '*',
          },
          {
            key: 'Access-Control-Allow-Methods',
            value: 'GET,POST,OPTIONS',
          },
          {
            key: 'Access-Control-Allow-Headers',
            value: 'Content-Type,Authorization',
          },
        ],
      },
    ];
  },
};

This method applies headers at build time for static routes or at the edge for dynamic routes, ensuring consistent CORS behavior without adding logic to your handlers.

Method 4: Server-Side Proxy Pattern

The most reliable way to eliminate CORS errors entirely is to avoid cross-origin requests in the browser altogether. By creating an API route that fetches the external API on the server side (Node.js or Edge runtime), the browser only communicates with your same-origin Next.js server.

// pages/api/proxy.ts
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const externalRes = await fetch('https://external-service.com/endpoint', {
    method: req.method,
    headers: {
      Authorization: `Bearer ${process.env.EXTERNAL_API_TOKEN}`,
    },
    body: req.method !== 'GET' && req.method !== 'HEAD' ? JSON.stringify(req.body) : undefined,
  });

  const data = await externalRes.json();
  res.status(externalRes.status).json(data);
}

Because the fetch operation executes in the server runtime, the browser's same-origin policy never encounters the external domain, bypassing CORS requirements completely.

Summary

  • packages/next/src/server/lib/router-utils/block-cross-site.ts contains the internal logic that blocks certain cross-site requests before they reach your code.
  • Use API route middleware with the cors package for route-specific CORS handling, utilizing the initMiddleware helper pattern.
  • Deploy Edge middleware to inject headers globally using NextResponse.next() for edge-optimized performance.
  • Configure static headers in next.config.js for declarative CORS policies on path patterns like /api/:path*.
  • Implement a server-side proxy to fetch external data from the server runtime, avoiding CORS checks entirely.
  • Reference test/integration/api-support/pages/api/cors.js for working integration tests that validate CORS header implementation.

Frequently Asked Questions

What causes CORS errors when fetching external APIs in Next.js?

CORS errors occur when a browser makes a request to a different origin and the response lacks the required Access-Control-Allow-Origin header. In Next.js applications, this typically happens when client-side JavaScript attempts to fetch a third-party API directly rather than through a Next.js API route that adds the proper headers.

Should I use a wildcard (*) for the Access-Control-Allow-Origin header?

Using * allows any domain to access your API, which is appropriate for public APIs but insecure for authenticated endpoints. For production applications requiring credentials, specify the exact origin (e.g., https://example.com) rather than a wildcard, and ensure your middleware dynamically validates the request origin against an allowlist.

How do I handle preflight OPTIONS requests in Next.js?

You must handle OPTIONS requests by returning a 200 or 204 status with the appropriate CORS headers before your main logic runs. In API routes, check if (req.method === 'OPTIONS') and return early. In Edge middleware, detect the method and return new NextResponse(null, { status: 204 }) to satisfy the browser's preflight check.

Can I disable CORS checks in Next.js development mode?

There is no built-in configuration to disable browser CORS checks entirely, as this is a browser security feature, not a Next.js restriction. However, you can configure your browser to disable CORS for local development using command-line flags, or more safely, proxy external requests through a local API route that adds the necessary headers as demonstrated in the examples above.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s https://instagit.com/install.md

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client