If you have ever built a frontend application with React, Next.js, Vue, Angular, or JavaScript and connected it to a backend API, you may have encountered an error like “Access to fetch at … has been blocked by CORS policy.”
This error can be confusing, especially when the API works perfectly in Postman or directly in the browser.
So, what is CORS, why does it exist, and how can developers fix CORS errors?
In this guide, we’ll explain CORS (Cross-Origin Resource Sharing) in simple terms, how CORS works, common CORS errors, HTTP headers, preflight requests, and how to configure CORS correctly in a Node.js and Express application.
What Is CORS?
CORS stands for Cross-Origin Resource Sharing.
CORS is a browser security mechanism that controls whether a web page from one origin can request resources from another origin.
For example, imagine your frontend is running at:
https://myapp.com
And your backend API is running at:
https://api.myapp.com
These are different origins, so the browser applies its cross-origin security rules when the frontend tries to communicate with the API.
Another common development setup is:
Frontend: http://localhost:3000
Backend: http://localhost:5000
Even though both applications are running on the same computer, they use different ports, so they have different origins.
This is where CORS becomes important.
What Is an Origin?
To understand CORS, you first need to understand the concept of an origin.
An origin consists of three parts:
Scheme + Host + Port
For example:
https://example.com:443
Here:
https= schemeexample.com= host443= port
Two URLs have the same origin only when all three parts match.
For example:
https://example.com/page
https://example.com/products
These are same-origin because the scheme, host, and port are the same.
But:
https://example.com
https://api.example.com
are different origins because the hosts are different.
Similarly:
http://localhost:3000
http://localhost:5000
are different origins because the ports are different.
Why Does CORS Exist?
CORS exists because browsers implement the same-origin policy, an important web security mechanism.
Without browser restrictions, a malicious website could potentially make requests to another website using a user’s browser and attempt to access sensitive information.
For example, imagine you are logged into an online banking website.
You then visit a malicious website.
Without appropriate browser protections, that malicious website could potentially attempt to make requests to the banking website and read sensitive responses.
The same-origin policy helps prevent websites from freely reading resources from other origins.
CORS provides a controlled mechanism for servers to tell browsers:
This particular origin is allowed to access my resources.
How Does CORS Work?
CORS works primarily through HTTP request and response headers.
Suppose your frontend is running at:
http://localhost:3000
and it requests data from:
http://localhost:5000/api/users
The browser sends information about the requesting origin:
Origin: http://localhost:3000
The backend can respond with:
Access-Control-Allow-Origin: http://localhost:3000
This tells the browser that the frontend origin is allowed to access the response.
The basic flow looks like this:
Frontend
http://localhost:3000
|
| HTTP Request
| Origin: http://localhost:3000
↓
Backend API
http://localhost:5000
|
| HTTP Response
| Access-Control-Allow-Origin:
| http://localhost:3000
↓
Browser
|
↓
Allow JavaScript to read response
If the server does not provide the required CORS response headers, the browser may block the frontend from accessing the response.
CORS Request Example
Imagine your React application sends:
fetch("http://localhost:5000/api/users")
.then((response) => response.json())
.then((data) => console.log(data));
The browser may send a request containing:
GET /api/users HTTP/1.1
Host: localhost:5000
Origin: http://localhost:3000
The server can respond:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:3000
Content-Type: application/json
Because the server explicitly allows the frontend origin, the browser permits JavaScript to access the response.
What Is a CORS Preflight Request?
One of the most important concepts when learning CORS is the preflight request.
A browser may send a preflight request before making certain cross-origin requests.
The preflight request uses the HTTP OPTIONS method.
For example:
OPTIONS /api/users HTTP/1.1
Origin: http://localhost:3000
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
The server can respond:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Authorization
If the browser determines that the requested operation is permitted, it can proceed with the actual request.
The process looks like:
Browser
|
| OPTIONS (Preflight)
↓
Server
|
| CORS Permission
↓
Browser
|
| Actual Request
↓
Server
|
| Response
↓
Browser
When Does CORS Preflight Happen?
Not every cross-origin request requires a preflight.
Certain requests can qualify as CORS-safelisted requests when they meet specific requirements.
A request may require preflight when it uses methods such as:
PUT
PATCH
DELETE
or uses certain non-safelisted request headers.
For example:
Authorization: Bearer token
or a request using a content type such as:
application/json
can result in a preflight request depending on the complete request configuration.
This is why developers sometimes see an OPTIONS request in the browser’s Network tab before their actual API request.
Important CORS Headers
Several HTTP headers are commonly used to configure CORS.
1. Access-Control-Allow-Origin
This header specifies which origin is allowed to access the resource.
Example:
Access-Control-Allow-Origin: https://myapp.com
A server can also use:
Access-Control-Allow-Origin: *
The wildcard allows broad cross-origin access for requests where credentials are not involved.
However, using * is not appropriate for every application.
2. Access-Control-Allow-Methods
This header specifies which HTTP methods are permitted for cross-origin requests.
Example:
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
3. Access-Control-Allow-Headers
This header specifies which request headers the browser may use for the cross-origin request.
Example:
Access-Control-Allow-Headers: Content-Type, Authorization
This is particularly relevant when your frontend sends an authorization token.
4. Access-Control-Allow-Credentials
This header indicates whether the browser is allowed to include credentials in a cross-origin request when the server permits them.
Example:
Access-Control-Allow-Credentials: true
Credentials can include things such as cookies.
When credentials are enabled, you generally cannot use:
Access-Control-Allow-Origin: *
Instead, the server should specify an allowed origin.
For example:
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Credentials: true
5. Access-Control-Expose-Headers
By default, browser JavaScript cannot necessarily access every response header.
The server can explicitly expose additional headers:
Access-Control-Expose-Headers: X-Total-Count
This can be useful when an API returns metadata through custom response headers.
CORS in Node.js and Express
If you are using Node.js with Express, one of the easiest ways to configure CORS is through the cors middleware.
Install it with:
npm install cors
Then configure it:
const express = require("express");
const cors = require("cors");
const app = express();
app.use(
cors({
origin: "http://localhost:3000",
})
);
app.use(express.json());
app.get("/api/users", (req, res) => {
res.json({
message: "Users fetched successfully",
});
});
app.listen(5000);
Now the backend allows requests from:
http://localhost:3000
Allowing Multiple Origins
In production applications, you may need to allow multiple frontend applications.
For example:
const allowedOrigins = [
"http://localhost:3000",
"https://myapp.com",
"https://www.myapp.com",
];
app.use(
cors({
origin: allowedOrigins,
})
);
Another approach is to dynamically validate the requesting origin:
const allowedOrigins = [
"http://localhost:3000",
"https://myapp.com",
];
app.use(
cors({
origin: function (origin, callback) {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error("Not allowed by CORS"));
}
},
})
);
This allows the server to maintain a specific allowlist rather than allowing every origin.
CORS With Credentials and Cookies
CORS becomes particularly important when your authentication system uses cookies.
Suppose your frontend is:
https://app.example.com
and your API is:
https://api.example.com
Your frontend may send:
fetch("https://api.example.com/profile", {
credentials: "include",
});
The server needs to explicitly support credentialed cross-origin requests.
For example:
app.use(
cors({
origin: "https://app.example.com",
credentials: true,
})
);
The response needs appropriate CORS headers, including:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Cookie behavior also depends on cookie attributes such as SameSite, Secure, domain, and path.
Therefore, fixing CORS alone may not be enough when cross-origin authentication uses cookies.
Why Does CORS Work in Postman but Not in the Browser?
This is one of the most common questions developers ask.
You may make an API request using Postman and receive:
{
"success": true
}
But when the same API is called from your React application, the browser reports a CORS error.
Why?
Because CORS is primarily enforced by web browsers.
Postman and similar API clients don’t enforce browser same-origin restrictions in the same way.
So this:
Postman → API
can work while:
Browser → Frontend → API
fails because the browser evaluates the CORS policy.
This is why testing an API successfully in Postman does not necessarily mean your frontend can call it successfully.
Common CORS Errors
You may encounter errors such as:
Access to fetch at 'https://api.example.com'
from origin 'http://localhost:3000'
has been blocked by CORS policy.
Another common message is:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
You may also see:
Response to preflight request doesn't pass access control check.
These messages generally indicate that the browser’s CORS requirements were not satisfied.
How to Fix CORS Errors
When you encounter a CORS error, don’t immediately add:
origin: "*"
Instead, identify exactly what the browser is requesting.
Step 1: Check the Frontend Origin
Open the browser’s Developer Tools and check the request.
For example:
http://localhost:3000
Step 2: Check the API URL
Determine which backend endpoint the frontend is calling:
http://localhost:5000/api/users
Step 3: Check the Network Tab
Look for:
OPTIONS
If an OPTIONS request appears before the actual request, you’re likely dealing with a preflight request.
Step 4: Check Response Headers
Look for headers such as:
Access-Control-Allow-Origin
Access-Control-Allow-Methods
Access-Control-Allow-Headers
Access-Control-Allow-Credentials
Step 5: Configure the Backend
Make sure the backend allows the required origin, methods, and headers.
CORS Error With Authorization Header
Suppose your frontend sends:
fetch("https://api.example.com/users", {
headers: {
Authorization: `Bearer ${token}`,
},
});
The browser may perform a preflight request because of the Authorization header.
Your backend may need to allow it:
Access-Control-Allow-Headers: Authorization, Content-Type
In Express:
app.use(
cors({
origin: "http://localhost:3000",
allowedHeaders: ["Content-Type", "Authorization"],
})
);
Without the appropriate CORS configuration, the browser may block the request before the actual API request is sent.
CORS vs Same-Origin Policy
These concepts are closely related but aren’t the same thing.
Same-Origin Policy
The same-origin policy is a browser security restriction that limits how resources from one origin can interact with resources from another origin.
CORS
CORS is a standardized mechanism that allows servers to specify which cross-origin requests browsers should permit.
A simple way to remember it is:
Same-Origin Policy
↓
Browser security restriction
↓
CORS
↓
Controlled cross-origin access
CORS vs CSRF
CORS and CSRF are sometimes confused because both involve cross-origin requests, but they solve different security problems.
CORS controls whether browser JavaScript can access cross-origin resources.
CSRF (Cross-Site Request Forgery) is an attack where an attacker attempts to make a user’s browser perform an unwanted action on a website where the user is authenticated.
For applications using cookie-based authentication, you need to think about both CORS and CSRF protection.
CORS should not be treated as a replacement for CSRF protection.
Is CORS a Backend Problem or Frontend Problem?
CORS is primarily controlled by the server, because the server tells the browser which origins and request characteristics are allowed.
The frontend can influence the request, but it generally cannot solve a server-side CORS configuration problem simply by changing JavaScript.
For example, changing:
fetch("https://api.example.com/users");
doesn’t give your frontend permission to access the API.
The backend must return appropriate CORS headers.
Should You Use Access-Control-Allow-Origin: *?
You can use:
Access-Control-Allow-Origin: *
for APIs where broad public cross-origin access is appropriate and credentials are not required.
However, avoid using a wildcard simply to hide a CORS error.
For private APIs, it is usually better to explicitly allow known origins:
Access-Control-Allow-Origin: https://app.example.com
This makes your CORS policy more intentional and easier to reason about.
CORS Best Practices
When configuring CORS, keep these practices in mind:
1. Allow Only Trusted Origins
Instead of allowing every origin, define an allowlist when your API is private.
2. Don’t Use Wildcards Without Understanding the Consequences
Especially when cookies or other credentials are involved.
3. Handle OPTIONS Requests Correctly
Your server, proxy, or API gateway must properly respond to preflight requests.
4. Allow Only Required Methods
Don’t enable every HTTP method unless your application needs them.
5. Allow Only Required Headers
Keep Access-Control-Allow-Headers as specific as practical.
6. Use HTTPS
CORS configuration does not replace transport security. Production applications should use HTTPS.
7. Test Production Origins
A configuration that works with:
http://localhost:3000
may not work with:
https://app.example.com
Make sure your production domains are explicitly considered.
CORS Example: React + Node.js
Imagine you have:
React Frontend
http://localhost:3000
Node.js API
http://localhost:5000
Your React application sends:
fetch("http://localhost:5000/api/products")
.then((res) => res.json())
.then((data) => console.log(data));
Your Express backend can allow the frontend:
const cors = require("cors");
app.use(
cors({
origin: "http://localhost:3000",
})
);
Now the browser knows that the backend permits requests from the React application’s origin.
The same concept applies when using Next.js, Vue, Angular, or other frontend frameworks.
Frequently Asked Questions About CORS
What does CORS stand for?
CORS stands for Cross-Origin Resource Sharing.
Why do I get a CORS error?
A CORS error usually means the browser’s cross-origin security checks were not satisfied by the server’s response.
Is CORS a security feature?
Yes. CORS works with browser security mechanisms to control cross-origin access.
Can I disable CORS in the frontend?
You should not try to solve a production CORS configuration problem by disabling browser security. The correct solution is generally to configure the server correctly.
Why does my API work in Postman but not React?
Because browser applications are subject to browser CORS and same-origin security rules, while Postman does not enforce those browser restrictions in the same way.
Does CORS protect my API from attackers?
CORS controls browser-based cross-origin access. It is not an authentication or authorization mechanism and should not be treated as a complete API security solution.
Does CORS apply to mobile applications?
Native mobile applications do not generally operate under the browser’s same-origin policy in the same way browser-based JavaScript applications do. CORS is primarily relevant to browser environments.
Final Thoughts
Understanding CORS is essential for modern web development because frontend and backend applications are frequently hosted on different origins.
The most important concepts to remember are:
Origin
↓
Same-Origin Policy
↓
CORS
↓
HTTP Headers
↓
Preflight Requests
↓
Controlled Cross-Origin Access
When a browser makes a cross-origin request, the server can use CORS response headers to tell the browser which origins, methods, headers, and credentials are permitted.
If you encounter a CORS error, check the browser’s Network tab, look for preflight OPTIONS requests, inspect the response headers, and verify the backend’s CORS configuration.
Most importantly, don’t treat CORS as something that should simply be disabled. A correctly configured CORS policy allows your frontend and backend to communicate while maintaining the browser’s security model.
Once you understand what CORS is, how preflight requests work, and how CORS headers control cross-origin requests, debugging frontend-to-backend API issues becomes much easier.




