CORS
CORS (Cross-Origin Resource Sharing) is a browser security mechanism that controls which origins (domain+protocol+port) can make HTTP requests to your server, preventing malicious websites from making authenticated requests on behalf of users.
Explanation
The Same-Origin Policy is a foundational browser security rule: JavaScript on page-a.com cannot make requests to api-b.com. This prevents malicious sites from using the victim's logged-in session to make authenticated API calls (CSRF). CORS is the controlled way to relax this restriction: the server explicitly whitelists which origins are allowed. When a browser makes a cross-origin request, it attaches an Origin header. If the server's response includes Access-Control-Allow-Origin: * (or the specific origin), the browser allows the response. If the header is missing or the origin isn't allowed, the browser blocks the response — the request was sent and processed by the server, but the browser prevents your JavaScript from reading the response. Preflight requests happen for "non-simple" requests (non-GET/POST, custom headers, JSON Content-Type): the browser first sends an OPTIONS request to check if the actual request is allowed. Only if the server responds with appropriate CORS headers does the browser send the actual request. This is why you see mysterious OPTIONS requests in your network tab. CORS is enforced by browsers, not servers. Server-to-server requests have no CORS restrictions. cURL and Postman have no CORS restrictions. Only browser JavaScript is subject to CORS — which is why your API works fine in testing but breaks in the browser. The fix is always on the server: configure CORS headers correctly for your allowed origins.
Code Example
javascript// Express.js CORS configuration (correct vs naive)
const cors = require('cors');
// BAD: allows all origins — fine for public APIs, wrong for authenticated APIs
app.use(cors());
// GOOD: explicitly whitelist allowed origins
const allowedOrigins = [
'https://yourapp.com',
'https://staging.yourapp.com',
process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : null,
].filter(Boolean);
app.use(cors({
origin: (origin, callback) => {
// Allow requests with no origin (mobile apps, curl, Postman)
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error(`Origin ${origin} not allowed by CORS`));
}
},
credentials: true, // required if sending cookies/auth headers
methods: ['GET','POST','PUT','PATCH','DELETE','OPTIONS'],
allowedHeaders: ['Content-Type','Authorization'],
}));
// Must respond to OPTIONS preflight
app.options('*', cors());
Why It Matters for Engineers
CORS errors are one of the most common and most confusing errors developers encounter. "No 'Access-Control-Allow-Origin' header is present" stops development cold. Understanding what CORS is, why it exists, and how to configure it correctly prevents hours of debugging incorrect "fixes" (like disabling CORS entirely, or using a proxy in development without understanding the production implications). CORS also matters for security: incorrectly setting Access-Control-Allow-Origin: * on an API that uses cookie-based authentication is a security vulnerability. The allow-all origin policy with credentials:true is the worst combination — browsers actually block this combination, but understanding why requires knowing what CORS protects against.
Related Terms
HTTP · REST API · Cookies · Middleware
Learn This In Practice
Go deeper with the full module on Beyond Vibe Code.
Web Development Fundamentals → →