Why You Keep Getting CORS Errors in Express and React (And How to Fix Them for Good)
If you have ever built a full-stack application with a React frontend and an Express backend, chances are you have run into the dreaded CORS error at least once. That red console message can stop your development flow dead in its tracks.
The good news? CORS errors are almost always a configuration problem, and once you understand the handful of scenarios that cause them, fixing them becomes straightforward. This guide skips the heavy theory and focuses on practical debugging so you can get back to building your app.
What Is a CORS Error in Express and React?
CORS stands for Cross-Origin Resource Sharing. Browsers enforce a security policy called the Same-Origin Policy, which blocks web pages from making requests to a different origin (a different domain, port, or protocol) than the one that served the page.
When your React app runs on http://localhost:5173 (Vite) or http://localhost:3000 (Create React App) and your Express server runs on http://localhost:9000, those are different origins. The browser will block the response unless your Express server explicitly tells the browser it is allowed.
A typical error message looks like this:
Access to XMLHttpRequest at 'http://localhost:9000/api/data' from origin 'http://localhost:5173' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Common Causes of CORS Errors (Quick Reference Table)
| Cause | Symptom | Fix Section |
|---|---|---|
| No CORS middleware installed | No Access-Control-Allow-Origin header in response |
Solution 1 |
| Origin mismatch in CORS config | Header value does not match the requesting origin | Solution 2 |
| Preflight (OPTIONS) request blocked | PUT, PATCH, DELETE, or requests with custom headers fail | Solution 3 |
| Credentials not configured | Cookies or auth headers not sent/received | Solution 4 |
| CORS middleware placed after route definitions | Some routes work, others do not | Solution 5 |
| Proxy not set up in React dev server | Works in production but not in development | Solution 6 |
Solution 1: Install and Configure the cors Middleware in Express
This is the most common fix and the first thing you should check. The official cors npm package makes it simple to add the right headers to every response.
Step 1: Install the package
npm install cors
Step 2: Add it to your Express app
const express = require('express');
const cors = require('cors');
const app = express();
// Enable CORS for all origins
app.use(cors());
app.get('/api/data', (req, res) => {
res.json({ message: 'CORS is working!' });
});
app.listen(9000, () => {
console.log('Server running on port 9000');
});
Calling cors() with no arguments allows all origins. This is fine during development but should be restricted in production (see Solution 2).
Solution 2: Fix the Origin Mismatch
A very common mistake is setting the allowed origin to the wrong value. For example, your React app runs on port 5173 but you allowed http://localhost:3000.
How to allow a specific origin
app.use(cors({
origin: 'http://localhost:5173'
}));
How to allow multiple origins
const allowedOrigins = [
'http://localhost:5173',
'http://localhost:3000',
'https://yourdomain.com'
];
app.use(cors({
origin: function (origin, callback) {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
}));
Tip: The !origin check allows requests with no origin header, such as server-to-server calls or tools like Postman.
Checklist to avoid origin mismatches
- Make sure the port number matches exactly.
- Do not include a trailing slash (use
http://localhost:5173, nothttp://localhost:5173/). - Match the protocol:
httpvshttps. - In production, use your actual domain, not
localhost.
Solution 3: Handle Preflight (OPTIONS) Requests Properly
Browsers send a preflight request (an HTTP OPTIONS request) before certain types of requests. This happens when:
- The HTTP method is
PUT,PATCH, orDELETE. - The request includes custom headers like
AuthorizationorContent-Type: application/json.
If your Express server does not respond to OPTIONS requests with the correct CORS headers, the actual request will never be sent.
The cors middleware handles this automatically
If you are using the cors package as shown in Solution 1, preflight requests are handled by default. However, if you have configured CORS headers manually, you need to explicitly handle OPTIONS:
app.options('*', (req, res) => {
res.header('Access-Control-Allow-Origin', 'http://localhost:5173');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.sendStatus(204);
});
Our recommendation: Stick with the cors npm package instead of setting headers manually. It handles preflight correctly and reduces the chance of errors.
Solution 4: Enable Credentials (Cookies and Auth Headers)
If your app uses cookies, sessions, or sends the Authorization header, you need to configure both the Express server and the React client.
Express side
app.use(cors({
origin: 'http://localhost:5173',
credentials: true
}));
Important: When credentials is set to true, you cannot use a wildcard (*) as the origin. You must specify the exact origin.
React side (using fetch)
fetch('http://localhost:9000/api/data', {
method: 'GET',
credentials: 'include'
});
React side (using axios)
axios.get('http://localhost:9000/api/data', {
withCredentials: true
});
Or set it globally:
axios.defaults.withCredentials = true;
Solution 5: Make Sure CORS Middleware Runs Before Your Routes
Express processes middleware in the order it is defined. If you place app.use(cors()) after your route definitions, those routes will never have the CORS headers applied.
Wrong order
app.get('/api/data', (req, res) => {
res.json({ data: 'hello' });
});
// Too late! The route above already handled the request.
app.use(cors());
Correct order
app.use(cors()); // This must come first
app.get('/api/data', (req, res) => {
res.json({ data: 'hello' });
});
This is a surprisingly common mistake, especially in larger codebases where middleware and routes are split across multiple files.
Solution 6: Use a Proxy in Your React Dev Server
An alternative to configuring CORS on the server during development is to use a proxy. The proxy makes your React dev server forward API requests to the Express server, so the browser sees everything as the same origin.
If you use Vite (React + Vite)
Edit vite.config.js:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
proxy: {
'/api': {
target: 'http://localhost:9000',
changeOrigin: true,
secure: false
}
}
}
});
Now in your React code, you can call /api/data instead of http://localhost:9000/api/data, and Vite will proxy the request.
If you use Create React App
Add a proxy field to your package.json:
{
"proxy": "http://localhost:9000"
}
Note: The proxy only works in development. In production, your React app is typically served as static files, so you still need CORS configured on your Express server (or serve both from the same origin).
Debugging CORS Errors: A Step-by-Step Workflow
When you hit a CORS error, follow these steps in order:
- Open the browser DevTools Network tab. Find the failed request and check the response headers. Look for
Access-Control-Allow-Origin. - Check if there is a preflight request. Look for an
OPTIONSrequest to the same URL. If it returns a non-2xx status code, your server is not handling it correctly. - Verify the origin values match. Compare the
Originrequest header with theAccess-Control-Allow-Originresponse header. They must be identical. - Check middleware order. Make sure
app.use(cors())appears before any route definitions in your Express code. - Check credentials. If you are sending cookies or auth headers, confirm that
credentials: trueis set on the server andwithCredentials: true(orcredentials: 'include') is set on the client. - Test with a tool like Postman or curl. These tools do not enforce CORS. If the request works in Postman but not in the browser, the issue is 100% a CORS header problem on your server.
CORS Configuration Cheat Sheet
| Scenario | Express Configuration |
|---|---|
| Allow all origins (dev only) | app.use(cors()) |
| Allow one specific origin | app.use(cors({ origin: 'http://localhost:5173' })) |
| Allow multiple origins | Use a function for the origin option (see Solution 2) |
| Allow credentials | app.use(cors({ origin: 'http://...', credentials: true })) |
| Allow specific HTTP methods | app.use(cors({ methods: ['GET', 'POST', 'PUT', 'DELETE'] })) |
| Allow custom headers | app.use(cors({ allowedHeaders: ['Content-Type', 'Authorization'] })) |
Production Tips: CORS Beyond Localhost
Once you deploy your React and Express apps, CORS needs are slightly different. Here are the key things to keep in mind:
- Never use
origin: '*'with credentials in production. This is a security risk and browsers will reject it anyway. - Use environment variables for allowed origins. This makes it easy to switch between staging and production values.
- Consider serving React from Express. If you serve your React build files using
express.static, both the frontend and API share the same origin. No CORS needed at all. - Review reverse proxy headers. If you use Nginx or a cloud load balancer in front of Express, make sure it is not stripping or overwriting CORS headers.
Frequently Asked Questions
What is CORS in React and Express?
CORS (Cross-Origin Resource Sharing) is a browser security mechanism that controls which origins can access resources on your Express server. When your React app and Express API run on different origins (different ports or domains), the browser blocks requests unless the server sends the correct CORS headers.
How do I enable CORS in Express?
Install the cors npm package and add app.use(cors()) at the top of your middleware stack, before any route definitions. To restrict access to specific origins, pass a configuration object: app.use(cors({ origin: 'https://yourdomain.com' })).
Why does my CORS error only happen in the browser and not in Postman?
CORS is enforced by web browsers only. Tools like Postman, curl, and server-side HTTP clients do not apply the Same-Origin Policy, so they never trigger CORS errors. If your request works in Postman but fails in the browser, the problem is that your server is not returning the right CORS headers.
How do I fix CORS errors when using axios in React?
First, make sure your Express server has CORS configured correctly. If you are sending cookies or auth tokens, add withCredentials: true to your axios request and set credentials: true in your Express CORS config. You can also set axios.defaults.withCredentials = true globally.
Can I use a proxy instead of configuring CORS?
Yes, during development. Both Vite and Create React App support proxy configuration that forwards API calls through the dev server, avoiding CORS entirely. However, this does not apply in production. You will still need proper CORS configuration on your Express server for deployed applications.
Why does my CORS setup work for GET requests but fail for POST or PUT?
POST requests with a Content-Type of application/json, as well as PUT and DELETE requests, trigger a preflight OPTIONS request. If your server does not handle that preflight correctly, the browser blocks the main request. Using the cors npm package handles this automatically.
Is it safe to allow all origins with cors()?
For local development, it is perfectly fine. For production, you should always specify the exact origins you want to allow. Allowing all origins in production can expose your API to unwanted cross-origin requests.
Wrapping Up
CORS errors in Express and React are one of the most common frustrations for full-stack JavaScript developers, but they all boil down to the server not sending the right headers. In most cases, installing the cors package, making sure it is loaded before your routes, and matching the origin value to your React app’s URL will solve the problem immediately.
Bookmark this guide for the next time that red console error shows up. With the solutions and debugging workflow above, you should be able to identify and fix the issue in minutes rather than hours.

