intermediate
CORS and preflight requests
Diagnose origin checks, simple versus preflighted requests, allowed headers/methods, credentials, caching, and server-side CORS policy.
CORS is enforced by browsers, not by curl or server-to-server calls. Simple cross-origin GET/POST may proceed without preflight; requests with custom headers, non-simple content types, or methods like PUT/DELETE trigger an OPTIONS preflight.
// Express example — reflect allowed origin, never * with credentials
app.use((req, res, next) => {
const origin = req.headers.origin;
if (allowedOrigins.has(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
}
if (req.method === 'OPTIONS') {
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
return res.status(204).end();
}
next();
});
The server must answer preflight before the browser sends the real request. `Access-Control-Expose-Headers` lists response headers JavaScript may read.
On interviews: debug "CORS error" as often a failed preflight or missing exposed header; explain why CORS is not a substitute for authentication.
Common pitfalls: reflecting any Origin in production; wildcard with credentials; caching preflight too aggressively after policy changes.
The trade-off is balancing simplicity, performance, safety, and operability — name which axis you optimized and what cost you accepted.
Checklist:
- Know simple vs preflighted request rules.
- Reflect specific origins when using cookies.
- Handle OPTIONS on API routes explicitly.
- Test from a real browser origin.