Cross‑Origin Resource Sharing (CORS) is the browser mechanism that controls whether a web page from one origin can call an API at another origin.
In .NET projects this shows up the moment your SPA (e.g., Vite/React on http://localhost:5173
) talks to an ASP.NET Core API running on a different port.
This guide explains correct, production‑ready CORS configuration in .NET 8, with copy‑paste snippets and pitfalls to avoid.
At a Glance
- Use an allow‑list of exact origins in dev and prod.
- Place
app.UseCors(...)
before endpoint mapping and typically before auth. - Do not combine
AllowAnyOrigin()
withAllowCredentials()
. - Use
SetIsOriginAllowedToAllowWildcardSubdomains()
for*.example.com
scenarios. - Enable CORS logs during troubleshooting.
OPTIONS
"preflight" to check methods/headers. Make sure your policy allows the method and headers you intend to use.
A safe dev baseline
var builder = WebApplication.CreateBuilder(args);
// 1) Register CORS policies
builder.Services.AddCors(options =>
{
options.AddPolicy("SpaDev", policy => policy
.WithOrigins("http://localhost:5173", "https://localhost:5173")
.AllowAnyHeader()
.AllowAnyMethod());
});
var app = builder.Build();
// 2) Apply CORS before endpoints/auth
app.UseCors("SpaDev");
app.MapControllers();
app.Run();
https://localhost:5173
is different from http://localhost:5173
.
Understanding the "open" snippet (and why it's risky)
// Example often seen during quick tests
app.UseCors(x => x
// .WithOrigins("http://localhost:5173", "https://localhost:5173")
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed(origin => true) // any origin
.AllowAnyOrigin() // any origin (no credentials)
// .AllowCredentials() // âš not valid together with AllowAnyOrigin()
);
Use an open policy only for short‑lived local testing or truly public APIs. If you need cookies or Authorization headers,
switch to a concrete allow‑list via .WithOrigins(...)
and .AllowCredentials()
.
Production pattern: configure via appsettings.json
var allowed = builder.Configuration
.GetSection("Cors:AllowedOrigins")
.Get<string[]>() ?? Array.Empty<string>();
builder.Services.AddCors(options =>
{
options.AddPolicy("Default", p => p
.WithOrigins(allowed)
.AllowAnyHeader()
.AllowAnyMethod()
// Add .AllowCredentials() only if you use cookies or Authorization headers
);
});
var app = builder.Build();
app.UseCors("Default");
app.MapControllers();
app.Run();
{
"Cors": {
"AllowedOrigins": [
"https://app.example.com",
"https://admin.example.com"
]
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.AspNetCore.Cors": "Information",
"Microsoft.EntityFrameworkCore": "Information"
}
}
}
Why enable CORS logs?
Setting "Microsoft.AspNetCore.Cors": "Information"
helps you see why a request was allowed or blocked while iterating.
Wildcard subdomains (*.example.com
)
ASP.NET Core supports wildcard subdomain matching via SetIsOriginAllowedToAllowWildcardSubdomains()
.
You still need to declare the wildcard origins explicitly with scheme(s) you support.
builder.Services.AddCors(options =>
{
options.AddPolicy("WildcardSubdomains", policy => policy
.WithOrigins(
"https://*.example.com",
"https://*.example.net",
"http://*.example.com" // add http if you need it
)
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyHeader()
.AllowAnyMethod()
// .AllowCredentials(); // valid with explicit origins (not with AllowAnyOrigin)
);
});
var app = builder.Build();
app.UseCors("WildcardSubdomains");
app.MapControllers();
app.Run();
Allow requests from any origin
Public APIs or quick local testing only.
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", policy => policy
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
});
var app = builder.Build();
app.UseCors("AllowAll");
(Advanced) Reflect the request Origin and allow credentials
*
(i.e., AllowAnyOrigin()
)
together with credentials. If you must support "any" origin with credentials, the only way is to
reflect the requesting origin (the server echoes the exact Origin), which is risky. Prefer explicit allow‑lists.
builder.Services.AddCors(options =>
{
options.AddPolicy("ReflectAnyOriginWithCreds", policy => policy
.SetIsOriginAllowed(_ => true) // reflect the incoming Origin (risky)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials());
});
var app = builder.Build();
app.UseCors("ReflectAnyOriginWithCreds");
Middleware order checklist
app.UseRouting()
app.UseCors(...)
app.UseAuthentication()
(if any)app.UseAuthorization()
app.MapControllers()
Troubleshooting
- Exact origin (scheme + host + port) is in your allow‑list.
- No trailing slash in
WithOrigins("https://app.example.com")
. - Preflight
OPTIONS
allowed? (method + headers). - CORS logs show policy applied (
Microsoft.AspNetCore.Cors
at Information). - Remember: server‑to‑server calls don't need CORS — only browsers enforce it.
References & Further Reading
- Microsoft Learn — Enable Cross‑Origin Requests (CORS) in ASP.NET Core: docs
- API —
SetIsOriginAllowedToAllowWildcardSubdomains
: docs - API —
SetIsOriginAllowed
: docs - MDN Web Docs — CORS overview: guide, and Preflight request: definition
- Discussion of reflect‑origin pattern (use with caution): example