Concept
Cross-Origin Resource Sharing (CORS)
Browsers enforce the Same-Origin Policy (SOP), which blocks web pages from making API requests to a different domain than the one that served the page.
CORS is an HTTP-header-based mechanism that allows servers to state which origins (domains) are permitted to read its API responses:
Client (origin: app.example.com) ──▶ GET /api (api.example.com)
Server Response includes: Access-Control-Allow-Origin: https://app.example.comPreflight Requests (OPTIONS)
For requests that modify data or use custom headers, the browser first sends an automatic preflight request using the OPTIONS method. The server must respond successfully, stating allowed methods and headers before the browser sends the actual request:
1. Browser: OPTIONS /api (checks CORS headers)
2. Server: 204 No Content (Access-Control-Allow-Methods: GET, POST)
3. Browser: POST /api (actual request is sent)Configuring CORS in Express
Use the standard cors middleware:
import express from 'express';
import cors from 'cors';
const app = express();
const corsOptions = {
origin: 'https://my-app.com', // Allow only this origin
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true, // Allow cookies to be sent
optionsSuccessStatus: 200 // For legacy browser compatibility
};
app.use(cors(corsOptions));Common Mistakes
1. Hardcoding wildcard origin: '*' alongside credentials
If your API configuration requires cookies or authorization headers, browsers forbid using Access-Control-Allow-Origin: *. If you set origin to wildcard and credentials to true, client fetch requests will fail. You must specify exact origin names.
2. Assuming CORS blocks servers from calling your API
CORS is a browser-only security feature. It does not prevent terminal utilities (like curl or Postman) or backend servers from calling your API. Protect sensitive endpoints using authentication, not CORS.
Best Practices
- Validate Origin Lists dynamically: Instead of static strings, use callback verification functions to validate subdomains or origin lists from databases:
const whitelist = ['https://app1.com', 'https://app2.com']; const corsOptions = { origin: (origin, callback) => { if (!origin || whitelist.includes(origin)) { callback(null, true); } else { callback(new Error('Blocked by CORS')); } } }; - : Set to support older browsers (like Internet Explorer 11) that do not parse the standard preflight response successfully.
