CORS Error Complete Guide - From Cause to Next.js/Express Setup
Problem
Calling an API from the frontend triggers this browser console error:
Access to fetch at 'http://api.example.com/data' from origin 'http://localhost:3000'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present
on the requested resource.
The server works fine — it’s only blocked in the browser.
Solution
CORS is a browser security policy that blocks requests to different origins. The fix is sending the right headers from the server.
Express
const cors = require('cors');
// Allow all origins (development only)
app.use(cors());
// Allow specific origins (production)
app.use(cors({
origin: ['https://myapp.com', 'https://www.myapp.com'],
credentials: true
}));
Next.js API Route
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/api/:path*',
headers: [
{ key: 'Access-Control-Allow-Origin', value: 'https://myapp.com' },
{ key: 'Access-Control-Allow-Methods', value: 'GET,POST,PUT,DELETE' },
{ key: 'Access-Control-Allow-Headers', value: 'Content-Type,Authorization' },
],
},
];
},
};
Next.js Rewrites (Proxy Approach)
// next.config.js
module.exports = {
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'http://api.example.com/:path*',
},
];
},
};
This makes the browser think the request goes to the same origin, so CORS never triggers.
Key Points
- CORS is a browser security policy, not a server bug. That’s why Postman and curl work fine but the browser doesn’t.
- Using
cors()with no options is fine for development. In production, always specify theoriginexplicitly. - When using
credentials: true, you cannot setorigin: '*'. A specific domain is required. - Next.js rewrites bypass CORS entirely by proxying requests through the same origin. It’s the simplest approach when you control the frontend.