gotchajavascriptMajor
CORS Preflight: OPTIONS Request Must Return Correct Headers Before Simple Requests Proceed
Viewed 0 times
preflightoptionscorsaccess-control-max-agesimple requestnon-simple
Error Messages
Problem
API returns 200 for GET requests but POST requests fail with CORS errors even though CORS headers are set, because the browser sends a preflight OPTIONS request that the server returns 404 or 405 for.
Solution
A preflight OPTIONS request is sent for any request that is not a 'simple request' (non-GET/HEAD/POST, or POST with JSON body, or custom headers). The server must respond to OPTIONS with the appropriate CORS headers and a 2xx status before the actual request is sent.
// In Express, handle OPTIONS before other middleware
app.options('*', cors(corsOptions)); // pre-flight across all routes
app.use(cors(corsOptions));
// Or manually
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,PATCH,OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type,Authorization,X-Request-ID');
res.setHeader('Access-Control-Max-Age', '86400'); // cache preflight 24h
if (req.method === 'OPTIONS') return res.sendStatus(204);
next();
});Why
The browser's CORS preflight is a safety check introduced before sending the 'real' request. If the preflight fails, the browser never sends the actual request.
Access-Control-Max-Age caches the preflight result, reducing round trips.Gotchas
- Adding a custom header (like X-Request-ID) to any request makes it non-simple and triggers a preflight.
- Authentication frameworks that return 401 on OPTIONS requests break CORS entirely.
- A 204 is preferred over 200 for OPTIONS responses (no body expected).
Access-Control-Max-Ageis capped by browsers (Chrome: 7200s, Firefox: 86400s).
Revisions (0)
No revisions yet.