Fix CORS Error in Next.js — API Routes and External APIs
Next.js API routes block cross-origin requests by default. If another domain calls your API routes, you need CORS headers. There are three ways to add them depending on your setup.
Browser Console Error
Access to fetch at 'https://yourproject.vercel.app/api/data' from origin 'https://yourapp.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.Option 1 — next.config.js (cleanest for all routes)
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/api/:path*',
headers: [
{ key: 'Access-Control-Allow-Origin', value: 'https://yourapp.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' },
],
},
];
},
};
This applies to all API routes matching the pattern. It does not handle OPTIONS preflight automatically — you still need to return 204 for OPTIONS in your handler.
Option 2 — middleware.ts (App Router, recommended)
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// Handle OPTIONS preflight
if (request.method === 'OPTIONS') {
return new NextResponse(null, {
status: 204,
headers: {
'Access-Control-Allow-Origin': 'https://yourapp.com',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400',
},
});
}
const response = NextResponse.next();
response.headers.set('Access-Control-Allow-Origin', 'https://yourapp.com');
response.headers.set('Access-Control-Allow-Credentials', 'true');
return response;
}
export const config = { matcher: '/api/:path*' };
Option 3 — per route (Pages Router)
// pages/api/data.js
export default function handler(req, res) {
res.setHeader('Access-Control-Allow-Origin', 'https://yourapp.com');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.setHeader('Access-Control-Allow-Credentials', 'true');
if (req.method === 'OPTIONS') {
return res.status(204).end();
}
res.json({ data: 'ok' });
}
Dynamic origin for multiple allowed domains
// middleware.ts — allowlist approach
const ALLOWED = ['https://yourapp.com', 'https://staging.yourapp.com'];
export function middleware(request: NextRequest) {
const origin = request.headers.get('origin') || '';
const allowedOrigin = ALLOWED.includes(origin) ? origin : '';
if (request.method === 'OPTIONS') {
return new NextResponse(null, {
status: 204,
headers: { 'Access-Control-Allow-Origin': allowedOrigin, ... }
});
}
}
Test your Next.js API CORS config →