How to Fix CORS Preflight Request Failed Error
DodaTech
2 min read
In this tutorial, you'll learn about How to Fix CORS Preflight Request Failed Error. We cover key concepts, practical examples, and best practices.
The Problem
Your frontend (e.g., a React app on localhost:3000) calls an API on another domain and gets Access to fetch at 'https://api.example.com/data' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check. The browser sends an OPTIONS preflight request before the actual request, and the server doesn't respond with the correct CORS headers.
Quick Fix
1. Enable CORS on the server (Express.js example)
const express = require('express');
const app = express();
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
return res.sendStatus(204);
}
next();
});
2. Enable CORS with the cors package (Express.js)
const cors = require('cors');
app.use(cors({
origin: 'http://localhost:3000',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
}));
3. Enable CORS on Nginx reverse proxy
server {
listen 80;
server_name api.example.com;
location / {
add_header Access-Control-Allow-Origin "http://localhost:3000" always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
add_header Access-Control-Allow-Credentials "true" always;
if ($request_method = OPTIONS) {
return 204;
}
proxy_pass http://backend:3000;
}
}
4. Enable CORS in Python (Flask)
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app, origins=["http://localhost:3000"], supports_credentials=True)
5. Test the preflight request manually
curl -X OPTIONS https://api.example.com/data \
-H "Origin: http://localhost:3000" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type" \
-v
Look for Access-Control-Allow-Origin: http://localhost:3000 in the response.
6. Handle credentials (cookies, auth headers)
// Server must use specific origin (not *), and set credentials: true
app.use(cors({
origin: 'http://localhost:3000',
credentials: true,
}));
Common Causes
| Cause | Behavior | Fix |
|---|---|---|
| No CORS headers on OPTIONS | Browser sends preflight, gets no CORS headers | Handle OPTIONS request explicitly |
Access-Control-Allow-Origin: * with credentials |
Browser requires specific origin when credentials: true |
Set explicit origin like http://localhost:3000 |
| Wrong HTTP methods | Preflight requests PUT but server only allows GET | Add Access-Control-Allow-Methods: PUT |
| Missing headers in preflight | Browser sends Authorization header but not in allowed list |
Add Authorization to Access-Control-Allow-Headers |
| Application layer doesn't handle OPTIONS | Framework returns 404 for OPTIONS | Add middleware to handle OPTIONS before route handler |
Prevention
- Restrict CORS to specific origins in production — never use
*with credentials - Return
204immediately forOPTIONSrequests to minimize latency - Test CORS with
curl -X OPTIONS -vbefore deploying frontend changes - Keep CORS configuration in both the application layer AND the reverse proxy for defense in depth
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro