
A React app can render perfectly, send a request that works in Postman, and still fail in the browser with a CORS message. That mismatch is the clue: React is rarely the component blocking the request. To fix CORS error in React, you need to understand the browser-to-server contract and apply the correction where it belongs – usually on the API, gateway, or development proxy.
CORS issues are frustrating because browser errors often make the frontend look guilty. The browser may report that a response lacks an `Access-Control-Allow-Origin` header, that a preflight request failed, or that credentials are not permitted. Each message points to a slightly different server policy problem.
What a CORS Error Actually Means
CORS stands for Cross-Origin Resource Sharing. An origin is made up of a protocol, host, and port. For example, `http://localhost:5173` and `http://localhost:3000` are different origins because their ports differ. Likewise, a React app served from `https://app.example.com` is cross-origin when it calls `https://api.example.com`.
Browsers enforce the same-origin policy to stop a malicious site from reading data from another site in the context of a logged-in user. CORS is the controlled exception. Your API tells the browser which origins may read its responses by returning specific HTTP headers.
This is why turning off browser security, installing a CORS extension, or adding `mode: ‘no-cors’` to `fetch` is not a real fix. Those options may hide the local symptom, but they do not create a usable, secure production policy. In particular, `no-cors` produces an opaque response that JavaScript cannot inspect.
Diagnose the Request Before Changing Configuration
Open browser developer tools and inspect the Network panel. Find both the failed request and, if present, an `OPTIONS` request immediately before it. The `OPTIONS` request is the preflight check. Before sending certain cross-origin requests, the browser asks the server whether the planned method, headers, and origin are allowed.
A preflight commonly occurs when you use methods such as `PUT`, `PATCH`, or `DELETE`, send JSON with `Content-Type: application/json`, include an `Authorization` header, or add custom headers. A simple `GET` may work while a JSON `POST` fails because only the second request triggers preflight.
Check four details before editing code: the exact frontend origin, the API response headers, whether `OPTIONS` returns a successful status, and whether a redirect occurs. A redirect from HTTP to HTTPS, an authentication redirect to a login page, or a proxy rewriting the path can produce an error that appears to be CORS but is really an infrastructure or routing issue.
Fix CORS Error in React at the API Layer
The preferred solution is to configure CORS on the server that owns the data. The API should return an allowlist rather than blindly permitting every origin. During local development, that may include `http://localhost:5173`; in production, it should include the actual deployed application URL.
For an Express API, the `cors` middleware is a common approach:
“`js import express from ‘express’; import cors from ‘cors’;
const app = express();
app.use(cors({ origin: [‘http://localhost:5173’, ‘https://app.example.com’], methods: [‘GET’, ‘POST’, ‘PUT’, ‘PATCH’, ‘DELETE’, ‘OPTIONS’], allowedHeaders: [‘Content-Type’, ‘Authorization’], credentials: true }));
app.use(express.json()); “`
The middleware must run before routes that need the policy. If a route, authentication layer, or reverse proxy rejects `OPTIONS` before CORS headers are attached, preflight will fail even though your main endpoint is configured correctly.
The equivalent principle applies to other backends. In Spring Boot, configure allowed origins and methods through the CORS configuration or security chain. In ASP.NET Core, register and apply a named CORS policy before the endpoint pipeline. In serverless environments, attach headers to both normal responses and `OPTIONS` responses. Framework syntax changes, but the browser requirements do not.
Avoid using `Access-Control-Allow-Origin: *` for authenticated APIs. It can be acceptable for a public, non-sensitive endpoint such as a public catalog or documentation feed. It cannot be combined with credentialed browser requests, and it is the wrong default for APIs that return user-specific data.
Credentials Need a Matching Policy
Cookie-based authentication adds one of the most common CORS traps. If your React request sends cookies, both sides must explicitly agree.
On the client, include credentials:
“`js fetch(‘https://api.example.com/profile’, { credentials: ‘include’ }); “`
On the API, return both `Access-Control-Allow-Credentials: true` and one specific allowed origin. The server cannot return `*` for `Access-Control-Allow-Origin` in this case. It must echo or select the approved requesting origin, such as `https://app.example.com`.
There is another layer: cookie attributes. Cross-site cookies generally need `SameSite=None; Secure`, which means HTTPS is required. If the cookie is not being sent, the browser may show an authentication failure rather than a classic CORS failure. Treat CORS headers, cookie settings, and identity-provider redirects as one request path, not isolated configuration tasks.
Bearer-token APIs are often simpler because the token is sent in the `Authorization` header rather than automatically by the browser. However, that header normally triggers preflight, so the API still needs to allow it explicitly.
Use a Development Proxy When It Fits
A development proxy lets the browser call the React development server with a same-origin path such as `/api/users`. The development server forwards that request to the real backend. This is useful when you do not control a local API configuration, when backend CORS policies are managed centrally, or when you want frontend code to use the same relative API path locally and in production.
In Vite, a proxy can be configured in `vite.config.js`:
“`js import { defineConfig } from ‘vite’; import react from ‘@vitejs/plugin-react’;
export default defineConfig({ plugins: [react()], server: { proxy: { ‘/api’: { target: ‘http://localhost:8080’, changeOrigin: true } } } }); “`
Your React code can then call `/api/users` instead of hard-coding `http://localhost:8080/api/users`. Create React App supports a similar development proxy concept, while Next.js and other full-stack frameworks can rewrite paths through their server layer.
A proxy is a development convenience, not a substitute for production API policy. If production sends the browser directly from one origin to another, the production API still needs correct CORS headers. If production routes `/api` through the same domain using a reverse proxy, then CORS may not be needed at all because the browser sees a same-origin request.
Check Gateways, CDNs, and Error Responses
Modern applications often have more than one server between React and the API. An API gateway may handle preflight while the application handles `POST` requests. A CDN can cache a response created for one origin and serve it to another. A web application firewall may block `OPTIONS`, and an ingress rule may not forward it.
When origins are dynamically allowed, make sure responses include `Vary: Origin`. That tells caches that the response can differ based on the request’s `Origin` header. Without it, a cache can deliver incorrect CORS headers across origins.
Also verify error responses. Teams sometimes attach CORS headers only to successful API responses. A `401`, `403`, `429`, or `500` without the expected headers becomes unreadable to the frontend and looks like a generic CORS failure. CORS middleware should cover the entire response path, including authentication and exception handling.
A Practical CORS Troubleshooting Sequence
When a request fails, follow the request from browser to edge to application. Start by copying the exact origin shown in developer tools. Confirm whether the browser sent preflight and whether the server answered it with the expected allowed origin, method, and headers. Then test the endpoint without the browser to distinguish API availability from browser policy.
Next, compare the working and failing requests. Differences in `Authorization`, JSON content type, cookies, redirects, or API hostnames often reveal the cause quickly. Finally, test the deployed environment separately from localhost. Local and production domains, HTTPS enforcement, cookies, gateways, and environment variables frequently change the behavior.
The fastest path to a durable fix is not making the browser less strict. It is making your API boundary explicit: name the clients you trust, allow only the methods and headers they need, and test that policy wherever the application actually runs.





