The "blocked by CORS policy" error is one of the most frustrating errors in web development. It happens when your browser blocks a request to a different domain because the server doesn't permit it. This guide covers every fix — from Express middleware to Nginx headers to development proxies.
What is a CORS Error?
CORS stands for Cross-Origin Resource Sharing. Browsers enforce a security rule called the Same-Origin Policy — a page on https://app.com cannot make requests to https://api.otherdomain.com unless the server explicitly allows it via HTTP response headers.
When those headers are missing or incorrect, the browser blocks the response and you see the error in your console. The server did receive and process your request — the browser just refuses to hand the response to your JavaScript code.
Typical CORS error messages:
- Access to fetch at '...' has been blocked by CORS policy
- No 'Access-Control-Allow-Origin' header is present on the requested resource
- Response to preflight request doesn't pass access control check
Common Causes
- Missing CORS headers: The server does not send
Access-Control-Allow-Originin its response. - Wrong origin value: The server allows
https://app.combut your request comes fromhttp://localhost:3000. - Preflight request fails: The server doesn't handle the
OPTIONSmethod or doesn't returnAccess-Control-Allow-Methods. - Credentials issue: You're sending cookies but the server doesn't set
Access-Control-Allow-Credentials: true(and you can't use*for the origin).
How to Fix CORS Error
1. Fix in Express / Node.js
Install the cors npm package and add it as middleware before your routes.
npm install cors
// Allow all origins (development only)
const cors = require('cors');
app.use(cors());
// Allow a specific origin (production)
app.use(cors({
origin: 'https://yourdomain.com',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true, // only if you need cookies/auth
}));
// Handle preflight for all routes
app.options('*', cors());Tip: Always call app.options('*', cors()) to handle preflight OPTIONS requests for all routes.
2. Fix in Python FastAPI
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["https://yourdomain.com"], # or ["*"] for dev
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)3. Fix in Nginx
location /api/ {
add_header 'Access-Control-Allow-Origin' 'https://yourdomain.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://yourdomain.com';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
}4. Fix CORS in Development (Use a Proxy)
Instead of changing the server, configure your dev server to proxy API requests — so the browser thinks it's talking to the same origin.
Vite (vite.config.ts):
export default {
server: {
proxy: {
'/api': {
target: 'http://localhost:8000',
changeOrigin: true,
rewrite: (path) => path.replace(/^/api/, ''),
},
},
},
}Create React App (package.json):
{
"proxy": "http://localhost:8000"
}Fix CORS Without Backend Access
If you can't modify the server (e.g., a third-party API), your options are limited but not zero:
- Use a CORS proxy for testing only — services like
https://corsproxy.ioforward your request and add CORS headers. Never use these in production with sensitive data. - Create a backend proxy route — have your own server forward the request to the third-party API. Your frontend calls your server (same origin), your server calls the API.
- Use Next.js API routes or Edge Functions — create
pages/api/proxy.tsto forward requests server-side.
Test your CORS headers live
Enter your API URL and origin to instantly check what CORS headers are returned.
Test your CORS headers live with our free CORS Tester5 CORS Error Messages Explained
| Error Message | Cause & Fix |
|---|---|
| blocked by CORS policy | Server missing Access-Control-Allow-Origin. Add CORS middleware on the server. |
| No 'Access-Control-Allow-Origin' header | Same as above — the response header is absent entirely. Check server configuration. |
| has been blocked by CORS policy: No 'Access-Control-Allow-Origin' | Chrome's full error message. The server returned a response but without the required header. |
| preflight response is invalid | Server doesn't handle OPTIONS requests. Add app.options('*', cors()) in Express. |
| Credentials flag is true but Access-Control-Allow-Credentials is not true | Add Access-Control-Allow-Credentials: true header and use a specific origin, not *. |
Frequently Asked Questions
What causes the blocked by CORS policy error?
The error occurs when a browser makes a cross-origin HTTP request and the server doesn't include theAccess-Control-Allow-Origin response header matching your frontend's origin. Browsers block the response as a security measure called the Same-Origin Policy.
How do I fix CORS in Express/Node.js?
Run npm install cors, then add app.use(cors({ origin: 'https://yourdomain.com' })) before your routes. Also call app.options('*', cors()) to handle preflight requests.
Can I fix CORS without touching the backend?
During development, use your build tool's proxy (Vite or CRA). For production, you need to control the server or add a backend proxy layer. CORS proxy services work for quick testing but should never handle sensitive data.
Why does localhost get CORS errors?
Each port on localhost is a different origin — localhost:3000 and localhost:8000 are separate. Your API server must explicitly allow your dev origin, or use a dev proxy to forward requests through the same port.
What is a preflight request?
A preflight is an automatic HTTP OPTIONS request sent by the browser before certain cross-origin requests (e.g., POST with JSON, or requests with custom headers like Authorization). The server must respond with status 200 and the correctAccess-Control-Allow-* headers.