aspnet-core-cors-cross-origin-resource-sharing-guia

Configuring CORS in ASP.NET Core

  • 4 min

The CORS is a mechanism by which the server authorizes certain cross-origin accesses in browsers.

Your API works perfectly. You make requests from Swagger and get a 200 OK. You make requests from Postman and it works flawlessly. Then you go to your React/Angular project, make a fetch to the same URL, and… BAM! The console is full of red letters:

Access to fetch at ‘https://mi-api.com’ from origin ‘http://localhost:3000’ has been blocked by CORS policy.

Why does your API hate your Frontend? Why can Postman do it but Chrome can’t? It’s not that your API is failing. It’s that the browser is “protecting” you (even though sometimes it seems like it just wants to annoy you).

Let’s understand why the block occurs and how to configure it in ASP.NET Core.

What is the Same-Origin Policy

To understand CORS, you first need to understand its opposite: the Same-Origin Policy.

For security reasons, browsers by default prevent a web page loaded from Origin A (e.g., localhost:3000) from reading data from a server on Origin B (e.g., localhost:5000).

An “Origin” is defined by: Protocol + Domain + Port.

  • http://example.com and https://example.com are different origins (protocol).
  • localhost:3000 and localhost:5000 are different origins (port).

If this rule didn’t exist, a malicious website could try to make requests to facebook.com in the background using your cookies and steal your session.

CORS to the rescue

CORS (Cross-Origin Resource Sharing) is the standard mechanism to relax this security in a controlled way. It’s how the server (your API) tells the browser: “Don’t worry, I know the folks from localhost:3000, let them through”.

Important: CORS is a policy applied by the browsers. Postman or a mobile app do not apply it, which is why they don’t suffer from this block.

CORS does not replace authentication nor does it prevent a non-browser client from calling the API.

Configuring CORS in ASP.NET Core

In .NET, configuring this is a two-step process: define the Policy (Services) and activate the Middleware.

Defining the policy (builder.Services)

In Program.cs, before builder.Build(), we define who can enter.

var builder = WebApplication.CreateBuilder(args);

// We define a constant for the name, to avoid mistakes later
var MyAllowedSpecificOrigins = "_myAllowedSpecificOrigins";

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowedSpecificOrigins,
                      policy =>
                      {
                          policy.WithOrigins("http://localhost:3000",
                                             "https://mi-web-produccion.com")
                                .AllowAnyHeader()
                                .AllowAnyMethod();
                      });
});
Copied!
  • WithOrigins: The whitelist of domains. Do not add a trailing /.
  • AllowAnyHeader: Important if you use JWT. If you don’t add this, the browser will block the Authorization header and login will fail.
  • AllowAnyMethod: Allows GET, POST, PUT, DELETE, etc.

Activating the middleware (app.UseCors)

Most people fail at this point. The order is critical.

CORS must run after UseRouting and before UseAuthorization. It is also common to place it before UseAuthentication, as in this example.

The browser may send a preliminary OPTIONS (preflight) request to ask if the method and headers are allowed. The CORS middleware must be able to resolve this before authorization protects the endpoint.

var app = builder.Build();

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

// ✅ HERE: Right before the security middleware
app.UseCors(MyAllowedSpecificOrigins);

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();
app.Run();
Copied!

Development and Production

During development, it can sometimes be tedious to add every new port. We can be more permissive ONLY in development.

if (app.Environment.IsDevelopment())
{
    // "Open bar" for development
    app.UseCors(x => x
        .AllowAnyOrigin()
        .AllowAnyMethod()
        .AllowAnyHeader());
}
else
{
    // Restrictive policy for production
    app.UseCors(MyAllowedSpecificOrigins);
}
Copied!

Danger: Do not use AllowAnyOrigin() as a default solution in production if your API handles sensitive data. Define specific origins and carefully review cases involving cookies or credentials.

The special case of credentials

If instead of JWT (Header) you decide to use Session Cookies or Windows Authentication, the configuration changes. The browser is even stricter.

  1. The frontend must send withCredentials: true.
  2. The backend CANNOT use AllowAnyOrigin(). It must specify the exact origin.
  3. The backend must add .AllowCredentials().
policy.WithOrigins("http://localhost:3000") // Explicit origin required
      .AllowAnyHeader()
      .AllowAnyMethod()
      .AllowCredentials(); // 👈 Allows passing Cookies
Copied!

Diagnosing CORS issues

If you still see the red error:

  1. Check the Network tab: Look for the failed request. Is it the actual request or an OPTIONS request?
  2. Review the order: Is UseCors placed before UseAuthorization?
  3. Check the Headers: Did you add .AllowAnyHeader()? The Authorization header (which carries the JWT) is often blocked if not explicitly allowed.
  4. Clear the cache: Sometimes browsers cache CORS responses. Open an incognito window.