How to Fix CORS Error in Express and React: Causes and Solutions Explained

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, not http://localhost:5173/).
  • Match the protocol: http vs https.
  • 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, or DELETE.
  • The request includes custom headers like Authorization or Content-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:

  1. Open the browser DevTools Network tab. Find the failed request and check the response headers. Look for Access-Control-Allow-Origin.
  2. Check if there is a preflight request. Look for an OPTIONS request to the same URL. If it returns a non-2xx status code, your server is not handling it correctly.
  3. Verify the origin values match. Compare the Origin request header with the Access-Control-Allow-Origin response header. They must be identical.
  4. Check middleware order. Make sure app.use(cors()) appears before any route definitions in your Express code.
  5. Check credentials. If you are sending cookies or auth headers, confirm that credentials: true is set on the server and withCredentials: true (or credentials: 'include') is set on the client.
  6. 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.