Fix CORS Errors on Vercel

Vercel CORS errors happen when your API routes don't return Access-Control-Allow-Origin — or when the OPTIONS preflight isn't handled. Two approaches: vercel.json for static header injection, or inline handlers for dynamic origin validation.

Option 1 — vercel.json (simplest)

Add a headers entry for your API path. This applies to every request automatically:

{ "headers": [ { "source": "/api/(.*)", "headers": [ { "key": "Access-Control-Allow-Origin", "value": "https://your-frontend.com" }, { "key": "Access-Control-Allow-Methods", "value": "GET, POST, PUT, DELETE, OPTIONS" }, { "key": "Access-Control-Allow-Headers", "value": "Content-Type, Authorization" }, { "key": "Access-Control-Max-Age", "value": "86400" } ] } ] }

Option 2 — Next.js API route handler

For dynamic origin validation or credentials, handle CORS inside the route:

// app/api/data/route.ts (App Router) const ALLOWED_ORIGINS = ['https://your-frontend.com', 'https://staging.your-frontend.com']; export async function OPTIONS(request: Request) { const origin = request.headers.get('origin') || ''; if (ALLOWED_ORIGINS.includes(origin)) { return new Response(null, { status: 204, headers: { 'Access-Control-Allow-Origin': origin, 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization', 'Access-Control-Max-Age': '86400', 'Vary': 'Origin', }, }); } return new Response(null, { status: 403 }); } export async function GET(request: Request) { const origin = request.headers.get('origin') || ''; const headers: Record<string, string> = {}; if (ALLOWED_ORIGINS.includes(origin)) { headers['Access-Control-Allow-Origin'] = origin; headers['Vary'] = 'Origin'; } return Response.json({ data: 'ok' }, { headers }); }

Using credentials (cookies / Authorization)

// Must use explicit origin — never * with credentials 'Access-Control-Allow-Origin': origin, // validated origin, not * 'Access-Control-Allow-Credentials': 'true',

Never combine Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true — browsers reject this.

Test your Vercel CORS config live → CORSFixer →