Express.js CORS 에러 해결 방법
A complete guide to understanding and fixing Cross-Origin Resource Sharing errors in Express.js applications.
Express.js CORS 에러 해결 방법
Introduction
CORS (Cross-Origin Resource Sharing) errors are the number one headache when developing full-stack applications with a separate frontend and backend. After building numerous APIs in Lisbon, I have compiled this definitive guide to understanding and fixing CORS issues in Express.js.
Environment
node --version
# v20.11.0
npm list express cors
# my-app@1.0.0
# +-- cors@2.8.5
# +-- express@4.18.2Problem
When your frontend at http://localhost:3000 makes a request to your backend at http://localhost:5000, the browser blocks the response and you see this error:
Access to XMLHttpRequest at 'http://localhost:5000/api/users' from origin
'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin'
header is present on the requested resource.Or for preflight requests:
Access to XMLHttpRequest at 'http://localhost:5000/api/users' from origin
'http://localhost:3000' has been blocked by CORS policy: Response to preflight request
doesn't pass access control check: It does not have HTTP ok status.Analysis
CORS is a security mechanism enforced by browsers. The server must explicitly allow cross-origin requests by sending the correct HTTP headers. Here is the flow:
Browser: OPTIONS /api/users (preflight)
Server: Must respond with Access-Control-Allow-Origin header
Browser: GET /api/users (actual request)
Server: Must include CORS headers in responseThe common CORS headers are:
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400Solution
Solution 1: Use the cors middleware (recommended)
const express = require('express');
const cors = require('cors');
const app = express();
// Allow all origins (development only)
app.use(cors());
app.listen(5000);Solution 2: Configure specific origins
const corsOptions = {
origin: 'http://localhost:3000',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400
};
app.use(cors(corsOptions));Solution 3: Dynamic origin validation
const allowedOrigins = [
'http://localhost:3000',
'https://myapp.com',
'https://www.myapp.com'
];
const corsOptions = {
origin: function (origin, callback) {
// Allow requests with no origin (mobile apps, curl, etc.)
if (!origin) return callback(null, true);
if (allowedOrigins.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true
};
app.use(cors(corsOptions));Solution 4: Manual CORS headers (without middleware)
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.setHeader('Access-Control-Allow-Credentials', 'true');
if (req.method === 'OPTIONS') {
return res.sendStatus(200);
}
next();
});Solution 5: Environment-based configuration
// cors.config.js
const whitelist = {
development: ['http://localhost:3000', 'http://localhost:5173'],
production: ['https://myapp.com']
};
const corsOptions = {
origin: function (origin, callback) {
const allowed = whitelist[process.env.NODE_ENV] || [];
if (!origin || allowed.includes(origin)) {
callback(null, true);
} else {
callback(new Error('CORS not allowed'));
}
},
credentials: true
};
module.exports = corsOptions;
// server.js
const cors = require('cors');
const corsOptions = require('./cors.config');
app.use(cors(corsOptions));Lessons Learned
- Always handle OPTIONS preflight requests - The browser sends these automatically
- Never use
Access-Control-Allow-Origin: *with credentials - This is forbidden by the spec - Set
Access-Control-Max-Ageto reduce preflight request overhead - Test with curl first to verify headers are present before blaming CORS
- Use environment variables for CORS origins in production deployments
This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.