Next.js CORS Configuration

Set up CORS headers in Next.js using next.config.js headers, middleware, and API route handlers. Covers static and dynamic origin patterns.

Framework Config

Detailed Explanation

CORS in Next.js — Three Approaches

Next.js offers multiple ways to set CORS headers depending on whether you need static or dynamic origin handling.

Approach 1: next.config.js headers()

Best for static origins. Headers are set at the edge/CDN layer.

// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: "/api/:path*",
        headers: [
          { key: "Access-Control-Allow-Origin", value: "https://app.example.com" },
          { key: "Access-Control-Allow-Methods", value: "GET, POST, PUT, DELETE, OPTIONS" },
          { key: "Access-Control-Allow-Headers", value: "Content-Type, Authorization" },
          { key: "Access-Control-Allow-Credentials", value: "true" },
          { key: "Access-Control-Max-Age", value: "3600" },
        ],
      },
    ];
  },
};

Approach 2: Middleware (Dynamic Origins)

Best for multiple origins or regex-based validation.

// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

const allowedOrigins = ["https://app.example.com", "https://admin.example.com"];

export function middleware(request: NextRequest) {
  const origin = request.headers.get("origin") ?? "";
  const isAllowed = allowedOrigins.includes(origin);

  // Handle preflight
  if (request.method === "OPTIONS") {
    return new NextResponse(null, {
      status: 204,
      headers: {
        "Access-Control-Allow-Origin": isAllowed ? origin : "",
        "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
        "Access-Control-Allow-Headers": "Content-Type, Authorization",
        "Access-Control-Max-Age": "3600",
        ...(isAllowed && { "Access-Control-Allow-Credentials": "true" }),
      },
    });
  }

  const response = NextResponse.next();
  if (isAllowed) {
    response.headers.set("Access-Control-Allow-Origin", origin);
    response.headers.set("Access-Control-Allow-Credentials", "true");
    response.headers.set("Vary", "Origin");
  }
  return response;
}

export const config = { matcher: "/api/:path*" };

Approach 3: Per-Route API Handler

Best for granular control per endpoint.

// app/api/data/route.ts
export async function OPTIONS() {
  return new Response(null, {
    status: 204,
    headers: {
      "Access-Control-Allow-Origin": "https://app.example.com",
      "Access-Control-Allow-Methods": "GET, OPTIONS",
      "Access-Control-Allow-Headers": "Content-Type",
      "Access-Control-Max-Age": "3600",
    },
  });
}

Which Approach to Choose

Need Approach
Static origin, all API routes next.config.js
Multiple/dynamic origins Middleware
Different CORS per endpoint Route handler

Use Case

A full-stack developer building a Next.js application where the frontend and API are in the same codebase but deployed on different domains (e.g., Vercel preview deployments with unique URLs). They need dynamic origin handling.

Try It — CORS Header Builder

Open full tool