Fix CORS Errors With Auth.js — API Routes and Session Endpoints
Updated April 2026
Reading this? Verify your fix live. Test your CORS config → CORSFixer
Auth.js CORS errors are usually not CORS problems. They are NEXTAUTH_URL mismatches or architecture issues appearing as CORS. Adding CORS headers to auth routes makes things worse. Here is the right approach.
The rule: do not add CORS to /api/auth routes
# Auth.js handles its own session management and CORS
# Adding custom CORS headers to these routes breaks things
# Wrong — breaks auth cookies
// middleware.ts
export function middleware(request: NextRequest) { if (request.nextUrl.pathname.startsWith("/api/auth")) { // Do NOT add CORS headers here const response = NextResponse.next(); response.headers.set("Access-Control-Allow-Origin", "*"); // ❌ breaks auth return response; }
}
Where to add CORS — your data API routes only
# These routes need CORS if called from a different domain: /api/users ✅ add CORS /api/products ✅ add CORS /api/data ✅ add CORS # These routes should NOT have custom CORS: /api/auth/session ❌ Auth.js handles this /api/auth/signin ❌ Auth.js handles this /api/auth/callback/* ❌ Auth.js handles this
Add CORS to Next.js data API routes
// middleware.ts — only for /api routes, not /api/auth
import { NextResponse } from "next/server";
export function middleware(request: NextRequest) { // Skip auth routes if (request.nextUrl.pathname.startsWith("/api/auth")) { return NextResponse.next(); } // Handle CORS for data API routes if (request.nextUrl.pathname.startsWith("/api/")) { 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", "Access-Control-Allow-Headers": "Content-Type, Authorization", }, }); } const response = NextResponse.next(); response.headers.set("Access-Control-Allow-Origin", "https://yourapp.com"); return response; } return NextResponse.next();
}
export const config = { matcher: "/api/:path*",
};
The most common mistake — wrong architecture
# Wrong: frontend on different domain calling Auth.js session endpoint
# Frontend: https://frontend.yourapp.com
# Backend: https://api.yourapp.com
# Auth.js session endpoint: https://api.yourapp.com/api/auth/session
# → Browser sends cross-origin request with cookie → CORS blocks it
# Right: frontend and Auth.js on same domain
# Frontend: https://yourapp.com
# Auth.js: https://yourapp.com/api/auth
# → Same-origin, no CORS needed for auth
# If you must have separate domains: use JWT sessions in Auth.js
export const { handlers, auth } = NextAuth({ session: { strategy: "jwt" }, // token-based, not cookie-based // This allows cross-domain auth with Authorization header
});
Debug Auth.js CORS errors
# Check 1: Is NEXTAUTH_URL set correctly? echo $NEXTAUTH_URL # should be https://yourapp.com # Check 2: Are the domains matching? # Browser is at: https://yourapp.com # Auth.js URL: https://yourapp.com ← must match # Check 3: What does the actual error say? # Open DevTools → Network → find the /api/auth request # Check: is it a CORS error or a 401/500 with CORS symptoms?Test your CORS config → CORSFixer