Skip to content

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 204 immediately for OPTIONS requests to minimize latency
  • Test CORS with curl -X OPTIONS -v before 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