DEV Community

Ahmed Omeiza
Ahmed Omeiza

Posted on

CORS Explained: Why Your Browser Blocks Your API

You build an API.

Your frontend sends a request.

The API responds successfully.

And somehow, the browser still says:

Blocked by CORS policy.

What just happened?

Let's break it down.

What Is CORS?

CORS (Cross-Origin Resource Sharing) is a browser security mechanism that controls whether a web page can make requests to a different origin.

An origin is made up of:

  • Protocol
  • Domain
  • Port

For example:

http://localhost:3000
Enter fullscreen mode Exit fullscreen mode

and

http://localhost:5000
Enter fullscreen mode Exit fullscreen mode

are different origins because their ports are different.

So if your React application runs on:

http://localhost:3000
Enter fullscreen mode Exit fullscreen mode

and your ASP.NET API runs on:

http://localhost:5000
Enter fullscreen mode Exit fullscreen mode

the browser treats the request as cross-origin.

Why Does CORS Exist?

Imagine a user is logged into their banking website.

Now imagine another website could freely make requests to that banking API using the user's browser.

That could create serious security problems.

The browser therefore follows the same-origin policy by default.

CORS provides a controlled way for servers to say:

"Requests from this particular origin are allowed."

A Simple Example

Suppose your frontend does this:

fetch("https://api.example.com/users")
Enter fullscreen mode Exit fullscreen mode

But your frontend is running on:

https://app.example.com
Enter fullscreen mode Exit fullscreen mode

The browser sees two different origins:

Frontend:
https://app.example.com

API:
https://api.example.com
Enter fullscreen mode Exit fullscreen mode

The API needs to explicitly allow the frontend's origin.

A server might respond with:

Access-Control-Allow-Origin: https://app.example.com
Enter fullscreen mode Exit fullscreen mode

The browser sees this header and knows that the frontend is allowed to access the response.

CORS in ASP.NET Core

In ASP.NET Core, you can configure CORS using a policy.

For example:

builder.Services.AddCors(options =>
{
    options.AddPolicy("FrontendPolicy", policy =>
    {
        policy
            .WithOrigins("https://app.example.com")
            .AllowAnyHeader()
            .AllowAnyMethod();
    });
});
Enter fullscreen mode Exit fullscreen mode

Then apply the policy:

app.UseCors("FrontendPolicy");
Enter fullscreen mode Exit fullscreen mode

Now the API allows requests from:

https://app.example.com
Enter fullscreen mode Exit fullscreen mode

What About AllowAnyOrigin()?

You might see this:

policy
    .AllowAnyOrigin()
    .AllowAnyHeader()
    .AllowAnyMethod();
Enter fullscreen mode Exit fullscreen mode

This means requests from any origin can access the API's CORS-enabled responses.

It's convenient during development, but you should be deliberate about using it in production.

If your application only needs to communicate with one frontend, explicitly specify that origin:

.WithOrigins("https://app.example.com")
Enter fullscreen mode Exit fullscreen mode

What Is a Preflight Request?

Sometimes the browser sends an OPTIONS request before the actual request.

This is called a preflight request.

For example, your frontend wants to send:

POST /api/users
Authorization: Bearer ...
Content-Type: application/json
Enter fullscreen mode Exit fullscreen mode

The browser may first ask the server:

OPTIONS /api/users
Enter fullscreen mode Exit fullscreen mode

with headers describing the intended request.

The server responds with something like:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Authorization, Content-Type
Enter fullscreen mode Exit fullscreen mode

If the browser is satisfied with the response, it sends the actual POST request.

So when debugging CORS, don't only look at the request you expected. Check whether an OPTIONS request is failing first.

CORS Is a Browser Restriction

This is one of the most important things to understand.

CORS is primarily enforced by browsers.

For example:

React → Browser → API
Enter fullscreen mode Exit fullscreen mode

The browser checks the CORS rules.

But if you make the same request using:

Postman → API
Enter fullscreen mode Exit fullscreen mode

you may not see the same CORS error.

That's because Postman isn't enforcing browser CORS rules in the same way.

So:

"It works in Postman but not in my frontend"

is often a strong clue that you're dealing with CORS.

CORS ≠ Authentication

CORS doesn't determine whether a user is authenticated.

These are different concerns.

Authentication asks:

Who are you?

Authorization asks:

Are you allowed to access this resource?

CORS asks:

Is this browser-based origin allowed to access the response?

For example, your API can correctly validate a JWT and still reject the browser's access because the CORS configuration doesn't allow the frontend's origin.

A Common Mistake

Developers sometimes see:

CORS error
Enter fullscreen mode Exit fullscreen mode

and immediately add:

.AllowAnyOrigin()
Enter fullscreen mode Exit fullscreen mode

That may hide the immediate problem, but it doesn't necessarily explain what caused it.

Instead, check:

  1. Is the frontend origin correct?
  2. Is the API returning the correct CORS headers?
  3. Is the OPTIONS preflight succeeding?
  4. Are the required headers allowed?
  5. Are the required HTTP methods allowed?
  6. Is middleware configured in the correct order?

Understanding the request flow is much more useful than simply disabling the restriction.

Key Takeaway

CORS isn't your API randomly refusing requests.

It's the browser enforcing a security boundary between different origins.

Once you understand origin, same-origin policy, preflight requests, and CORS response headers, those confusing:

Blocked by CORS policy
Enter fullscreen mode Exit fullscreen mode

errors become much easier to debug.

Don't just add AllowAnyOrigin() because the error disappeared. Understand why the browser rejected the request in the first place.

Top comments (0)