How to Fix CORS Preflight Errors
Before sending a non-simple cross-origin request (such as a POST with application/json or custom Authorization headers), the browser sends an automatic HTTP OPTIONS preflight probe. If the server fails to handle OPTIONS, returns a 4xx/5xx status, or omits requested headers, the actual request is never dispatched.
Response to preflight request doesn't pass access control check: It does not have HTTP ok status. / Method Not Allowed (405).Why does How to Fix CORS Preflight Errors occur?
Primary Root Cause
The W3C CORS specification requires browsers to send a "preflight" probe before executing any request that could potentially modify server state or send non-standard headers.
A request triggers preflight if it uses methods other than GET/POST/HEAD, Content-Type other than text/plain, multipart/form-data, or application/x-www-form-urlencoded, or custom headers like Authorization. The browser sends OPTIONS with Access-Control-Request-Method and Access-Control-Request-Headers. The server must explicitly validate these headers.
How to Confirm You Are Facing This Exact Issue
Two requests appear in the Network tab for a single fetch() call: the first is an OPTIONS request marked in red, and the second is canceled.
Console error: "Response to preflight request doesn't pass access control check: It does not have HTTP ok status".
Console error: "Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response".
Network tab shows status 405 Method Not Allowed or 404 Not Found on the OPTIONS request.
Explicit Diagnostic Reasoning
Inspect the failed OPTIONS request in your browser DevTools Network tab:
"If the OPTIONS request returns HTTP 405 Method Not Allowed"
Your web server or API framework is configured to reject OPTIONS requests on that route.
→ Register an explicit OPTIONS route handler or enable global CORS middleware.
"If the error says "Request header field X is not allowed by Access-Control-Allow-Headers""
Your frontend is sending a custom header (e.g., Authorization, X-Api-Key) not listed in the server’s Access-Control-Allow-Headers.
→ Add that header name to Access-Control-Allow-Headers on the server.
"If the OPTIONS request returns HTTP 401 Unauthorized"
Your authentication middleware is intercepting the OPTIONS request and demanding a token.
→ Configure your auth middleware to bypass authentication for HTTP OPTIONS requests.
Simulates a browser preflight probe. Check for HTTP 200/204 status and Access-Control-Allow-Headers containing authorization.
Long-Term Engineering Solution
Configure your server to intercept all incoming HTTP OPTIONS requests, verify that the requested method and headers are permitted, and return an immediate 204 No Content response with full CORS response headers.
Architectural Pattern
Handle preflights at the network perimeter (API Gateway or Nginx) before requests reach application code, caching the decision with Access-Control-Max-Age.
How do you fix this step-by-step?
Bypass Auth Middleware on OPTIONS
Ensure your JWT or session authentication guard ignores OPTIONS requests. Preflight requests never carry cookies or credentials.
// Express auth middleware guard
export function authGuard(req, res, next) {
// Always allow preflight OPTIONS requests through without checking auth!
if (req.method === 'OPTIONS') {
return next();
}
// Standard token validation...
}Include All Required Custom Headers
Explicitly specify Content-Type, Authorization, and any custom headers in Access-Control-Allow-Headers.
Cache the Preflight Check
Add Access-Control-Max-Age: 86400 (24 hours). This tells browsers not to send repeated OPTIONS requests for every single subsequent API call.
Return HTTP 204 No Content
Preflights require no body payload; a 204 No Content status terminates the preflight check cleanly.
What are common misconceptions and mistakes when fixing this?
Requiring authentication on OPTIONS preflight endpoints
Perché fallisce: Browsers strictly refuse to attach Authorization tokens or cookies to preflight OPTIONS requests.
✓ Alternativa corretta: Allow OPTIONS requests to pass through unauthenticated.
Returning HTTP 405 Method Not Allowed on OPTIONS
Perché fallisce: The browser treats any non-2xx status as a failed preflight, immediately aborting the real request.
✓ Alternativa corretta: Ensure all routers respond to OPTIONS with status 200 or 204.
Can this fix introduce security vulnerabilities?
Preflight requests prevent malicious websites from triggering state-changing actions (like DELETE or PUT) on internal or third-party APIs.
Security Risks If Ignored
- •Blindly returning Access-Control-Allow-Headers: * can allow malicious scripts to send unexpected headers.
Hardening Best Practices
- ✓Explicitly whitelist allowed methods: Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS.
- ✓Whitelist only needed headers: Access-Control-Allow-Headers: Content-Type, Authorization.
Complete CORS Preflight Solution in Node.js / Express
// BROKEN: Routes reject OPTIONS; auth guard blocks preflight
app.use((req, res, next) => {
if (!req.headers.authorization) return res.status(401).send('Unauthorized');
next();
});
app.post('/api/orders', (req, res) => res.json({ success: true }));// FIXED: Comprehensive CORS preflight handling with caching
import cors from 'cors';
app.use(cors({
origin: 'https://app.example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
credentials: true,
maxAge: 86400, // Cache preflight for 24 hours
}));
// Express handles OPTIONS automatically via cors middleware!Frequently Asked Questions About How to Fix CORS Preflight Errors
Can you avoid preflight requests entirely?↓
Yes, if your request meets the definition of a "Simple Request".
A Simple Request uses GET, POST, or HEAD, contains only standard headers (Accept, Accept-Language, Content-Language), and has Content-Type limited to application/x-www-form-urlencoded, multipart/form-data, or text/plain. However, modern JSON APIs using application/json or Authorization headers always mandate preflights.
Why does my API feel slow on every user click?↓
If you omit Access-Control-Max-Age, the browser sends an OPTIONS preflight before EVERY single API call, effectively doubling your network latency.
Adding Access-Control-Max-Age: 86400 allows the browser to cache the preflight check for 24 hours, restoring instant single-trip API performance.