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.