How to Fix CORS Errors in REST API Development
If you have ever built a frontend that calls a REST API, chances are you have seen this dreaded message in your browser console:
Access to fetch at 'https://api.example.com/data' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
It stops your application dead in its tracks. Nothing renders. Your API call fails silently or throws an ugly error. And the worst part? Your API works perfectly fine in Postman or cURL.
This guide will explain what CORS errors are, why they happen, and walk you through the most common fixes across Node.js (Express), Python (Flask), and browser-based requests. Whether you are building your first REST API or debugging a production issue, this post covers everything you need.
What Is CORS Exactly?
CORS stands for Cross-Origin Resource Sharing. It is a security mechanism built into web browsers that controls how web pages from one origin (domain, protocol, or port) can request resources from a different origin.
An “origin” is defined by three components:
- Protocol (http vs https)
- Domain (example.com vs api.example.com)
- Port (3000 vs 8080)
If any of these differ between the page making the request and the server receiving it, the browser treats it as a cross-origin request and enforces CORS rules.
Why Does CORS Exist?
CORS exists to protect users. Without it, any malicious website could make requests to your bank’s API using your cookies and session data. The browser’s Same-Origin Policy blocks this by default. CORS is the mechanism that allows servers to selectively relax this restriction for trusted origins.
What Causes CORS Errors?
A CORS error occurs when the browser sends a cross-origin request and the server’s response does not include the proper CORS headers. Here are the most common causes:
| Cause | Description |
|---|---|
| Missing Access-Control-Allow-Origin header | The server response does not include this header at all. |
| Origin mismatch | The header exists but does not match the requesting origin (e.g., the server allows https://app.com but the request comes from http://localhost:3000). |
| Preflight request failure | The browser sends an OPTIONS request first, and the server does not handle it correctly. |
| Disallowed HTTP method | The server does not include the method (PUT, DELETE, PATCH) in Access-Control-Allow-Methods. |
| Disallowed custom headers | The request includes headers (like Authorization) not listed in Access-Control-Allow-Headers. |
| Credentials not allowed | The request sends cookies or auth tokens but the server does not set Access-Control-Allow-Credentials: true. |
| Wildcard with credentials | The server responds with Access-Control-Allow-Origin: * while credentials are included, which browsers reject. |
Understanding Preflight Requests
Before diving into fixes, it is important to understand preflight requests because they are responsible for a large percentage of CORS errors developers encounter.
When your browser determines a request is “not simple” (for example, it uses PUT, DELETE, or includes custom headers like Authorization), it first sends an OPTIONS request to the server. This is the preflight. The server must respond to this OPTIONS request with the appropriate CORS headers. Only then will the browser proceed with the actual request.
A request triggers a preflight if it meets any of these conditions:
- Uses a method other than GET, HEAD, or POST
- Includes headers beyond the basic set (Accept, Content-Type with limited values, etc.)
- Uses
Content-Typewith a value other thanapplication/x-www-form-urlencoded,multipart/form-data, ortext/plain
If your server does not respond to OPTIONS requests properly, the preflight fails and your actual request never gets sent.
How to Fix CORS Errors: Step-by-Step Solutions
Now let’s get into the practical fixes. We will cover server-side solutions (the correct approach), development workarounds, and framework-specific implementations.
Fix 1: Add CORS Headers on the Server (The Right Way)
The proper solution to a CORS error is always to configure the server to send the correct response headers. This tells the browser which origins, methods, and headers are allowed.
The key headers you need to set are:
Access-Control-Allow-Origin– Which origins can access the resourceAccess-Control-Allow-Methods– Which HTTP methods are permittedAccess-Control-Allow-Headers– Which request headers are permittedAccess-Control-Allow-Credentials– Whether cookies/auth can be sentAccess-Control-Max-Age– How long the preflight result can be cached
Node.js / Express
The easiest approach in Express is to use the cors middleware package:
npm install cors
Basic usage (allows all origins):
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
app.get('/api/data', (req, res) => {
res.json({ message: 'CORS is working!' });
});
app.listen(8080);
For production, you should restrict the allowed origins:
const corsOptions = {
origin: ['https://yourfrontend.com', 'https://app.yourfrontend.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400
};
app.use(cors(corsOptions));
Manual setup without the cors package:
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'https://yourfrontend.com');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.header('Access-Control-Allow-Credentials', 'true');
// Handle preflight
if (req.method === 'OPTIONS') {
return res.sendStatus(204);
}
next();
});
Python Flask
Install the Flask-CORS extension:
pip install flask-cors
Basic usage:
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
@app.route('/api/data')
def get_data():
return {'message': 'CORS is working!'}
Restricted configuration for production:
CORS(app, resources={
r"/api/*": {
"origins": ["https://yourfrontend.com"],
"methods": ["GET", "POST", "PUT", "DELETE"],
"allow_headers": ["Content-Type", "Authorization"],
"supports_credentials": True
}
})
Django (Django REST Framework)
Install django-cors-headers:
pip install django-cors-headers
Add to your settings.py:
INSTALLED_APPS = [
...
'corsheaders',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...
]
CORS_ALLOWED_ORIGINS = [
'https://yourfrontend.com',
]
CORS_ALLOW_CREDENTIALS = True
Fix 2: Handle Preflight (OPTIONS) Requests Properly
If you are setting headers manually and still getting CORS errors on PUT, DELETE, or requests with custom headers, the issue is almost certainly the preflight OPTIONS request.
Make sure your server:
- Responds to OPTIONS requests on the same endpoint
- Returns a 200 or 204 status code (not 404 or 500)
- Includes all CORS headers in the OPTIONS response
If you use a framework middleware like cors for Express or flask-cors, this is handled automatically. If you configure things manually, you must explicitly handle OPTIONS.
Fix 3: Use a Proxy During Development
During local development, your frontend often runs on localhost:3000 while your API runs on localhost:8080. Instead of configuring CORS just for development, you can use a development proxy so the browser thinks everything comes from the same origin.
React (Create React App / Vite)
In vite.config.js:
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
}
}
}
});
Now your frontend can call /api/data instead of http://localhost:8080/api/data, and the dev server proxies the request. No CORS issue at all.
Angular
Create a proxy.conf.json:
{
"/api": {
"target": "http://localhost:8080",
"secure": false,
"changeOrigin": true
}
}
Then run: ng serve --proxy-config proxy.conf.json
Fix 4: Use a Serverless Function or Backend Proxy
If you are consuming a third-party API that you do not control and it does not send CORS headers, you cannot fix this on the client side. The solution is to route the request through your own backend or a serverless function.
The flow looks like this:
- Your frontend calls your own server:
GET /api/proxy/weather - Your server calls the third-party API (server-to-server, no CORS applies)
- Your server returns the data to the frontend with proper CORS headers
This works because CORS is a browser-only restriction. Server-to-server HTTP requests are not subject to CORS rules.
Example with a serverless function (e.g., Vercel, AWS Lambda, Cloudflare Workers):
// Vercel serverless function: /api/proxy.js
export default async function handler(req, res) {
const response = await fetch('https://thirdparty-api.com/data', {
headers: { 'Authorization': 'Bearer YOUR_KEY' }
});
const data = await response.json();
res.setHeader('Access-Control-Allow-Origin', '*');
res.status(200).json(data);
}
Fix 5: Quick Development Workarounds (Not for Production)
These are temporary workarounds for local development only. Never rely on these in production.
| Workaround | How | Risk |
|---|---|---|
| Browser extension | Install a CORS-unblock extension in Chrome or Firefox | Disables security for all sites while active |
| Disable browser security | Launch Chrome with --disable-web-security flag |
Completely disables Same-Origin Policy |
| Public CORS proxy | Route requests through a service like cors-anywhere | Third party sees all your traffic, rate limits apply |
Warning: Using --disable-web-security or CORS extensions leaves your browser vulnerable. Use a separate browser profile for development if you go this route, and never browse the web with security disabled.
Common CORS Error Messages and What They Mean
Here is a quick reference table to help you diagnose specific error messages:
| Error Message | What It Means | Fix |
|---|---|---|
| No ‘Access-Control-Allow-Origin’ header is present | Server is not sending CORS headers at all | Add CORS middleware or headers on the server |
| The value of the ‘Access-Control-Allow-Origin’ header must not be the wildcard ‘*’ | You are sending credentials but using a wildcard origin | Replace * with the specific origin |
| Method PUT is not allowed by Access-Control-Allow-Methods | The server does not list PUT in allowed methods | Add PUT to Access-Control-Allow-Methods |
| Request header field authorization is not allowed | The Authorization header is not in allowed headers | Add Authorization to Access-Control-Allow-Headers |
| Response to preflight request doesn’t pass access control check | The OPTIONS request failed or returned wrong status | Ensure OPTIONS returns 200/204 with correct headers |
CORS Debugging Checklist
When you hit a CORS error, run through this checklist before searching for more complex solutions:
- Open the browser DevTools Network tab and look at the actual request and response headers
- Check if there is an OPTIONS preflight request and whether it succeeded (status 200 or 204)
- Verify the response includes
Access-Control-Allow-Originwith the correct origin value - If using credentials (cookies, Authorization header), confirm the server sets
Access-Control-Allow-Credentials: trueand does NOT use wildcard*for the origin - Check the request method and headers against what the server allows
- Test the API directly with cURL or Postman to confirm the API itself works (these tools do not enforce CORS)
- Check middleware order in your server framework. CORS middleware must run before your route handlers.
- Look for typos in origins such as a trailing slash (
https://example.com/vshttps://example.com)
Best Practices for CORS in Production
Once you have fixed your CORS error, make sure your configuration is secure and maintainable:
- Never use
Access-Control-Allow-Origin: *in production if your API handles authentication or sensitive data. Always whitelist specific origins. - Keep your allowed origins list in environment variables so you can manage different values for development, staging, and production.
- Set
Access-Control-Max-Ageto cache preflight responses and reduce the number of OPTIONS requests. A value of 86400 (24 hours) is common. - Only allow the HTTP methods and headers your API actually needs. Do not open everything just to make errors go away.
- If you use a CDN or reverse proxy (Nginx, Cloudflare, AWS ALB), check that it is not stripping or overwriting your CORS headers.
- Log preflight failures on your server so you can identify misconfiguration early.
CORS Errors with Specific Frontend Frameworks
React (fetch / axios)
CORS errors in React projects are almost always a server issue. React itself does not control CORS. However, a common mistake is setting headers on the client request thinking it will fix CORS:
// This does NOT fix CORS!
fetch('https://api.example.com/data', {
headers: {
'Access-Control-Allow-Origin': '*' // Wrong! This is a response header.
}
});
Access-Control-Allow-Origin is a response header that must be set by the server. Setting it on the client request does nothing.
If you are working locally, use the Vite proxy or CRA proxy configuration shown above.
Angular (HttpClient)
Same principle applies. Use the Angular dev proxy for local development, and ensure your backend sets the proper CORS headers for production.
JavaScript Fetch API
When using the Fetch API with credentials:
fetch('https://api.example.com/data', {
method: 'GET',
credentials: 'include' // Sends cookies
});
The server must respond with:
Access-Control-Allow-Origin: https://yourfrontend.com (NOT *)
Access-Control-Allow-Credentials: true
Frequently Asked Questions
What causes CORS errors?
CORS errors are caused by the browser blocking a cross-origin HTTP request because the server’s response does not include the required CORS headers. This happens when your frontend and backend are on different origins (different domain, protocol, or port).
How do I resolve a CORS error?
The correct fix is to configure your server to return the appropriate CORS headers, specifically Access-Control-Allow-Origin. Use a CORS middleware for your framework (such as the cors npm package for Express or flask-cors for Flask). For third-party APIs you do not control, use a backend proxy or serverless function.
How to get rid of CORS errors in Chrome?
For development, you can use Chrome with the --disable-web-security flag or install a CORS extension. However, these are temporary workarounds. The real fix is always on the server side. For production, you must configure proper CORS headers on your API server.
How to bypass CORS errors locally?
The best approach for local development is to use a dev server proxy (available in Vite, Webpack Dev Server, and Angular CLI). This routes your API requests through the same origin as your frontend, eliminating CORS entirely during development without any security compromise.
Why does my API work in Postman but not in the browser?
Postman and cURL are not browsers, so they do not enforce the Same-Origin Policy or CORS. They send the request directly without any preflight check. Browsers enforce CORS to protect end users, which is why the same request fails in a browser but works in Postman.
Can I fix CORS errors from the frontend?
No. CORS is enforced by the browser based on the server’s response headers. You cannot fix it by adding headers to your frontend request. The fix must come from the server or by using a proxy that you control.
Wrapping Up
CORS errors are one of the most common and frustrating issues in REST API development, but they follow predictable patterns. In almost every case, the solution is to configure your server correctly. Use framework-specific CORS middleware, handle preflight OPTIONS requests, and avoid wildcard origins when credentials are involved.
For local development, proxy configurations are your best friend. For third-party APIs, route requests through your own backend. And whatever you do, avoid disabling browser security in production.
At Pixelseed, we build REST APIs and web applications with security and developer experience in mind. If you are struggling with CORS or any other API integration challenge, feel free to reach out.