โ๏ธ CORS Works in Dev, Dies in Production
Your frontend calls your ASP.NET Core API fine on localhost, then throws a CORS error the moment both are deployed — even though you configured a CORS policy. The gap is almost always a mismatch between what you configured and what the browser actually sees as the origin.
๐ The Problem
// This looks reasonable, and works on localhost...
builder.Services.AddCors(options =>
{
options.AddPolicy("Default", policy =>
policy.WithOrigins("http://localhost:3000")
.AllowAnyHeader().AllowAnyMethod());
});
// ...but production's frontend is served from
// https://app.example.com, not localhost:3000 - so every
// production request gets silently blocked by the browser.
๐ The Other Common Causes
1. Origin mismatch: the policy lists a dev URL only, or the production URL has a typo, wrong scheme (http vs https), or a trailing slash that doesn't match exactly. 2. Reverse proxy (nginx/IIS/Cloudflare) stripping or rewriting the Access-Control-* response headers before they reach the browser - CORS is enforced by the BROWSER based on response headers, so if a proxy drops them, it fails even though your app set them correctly. 3. AllowCredentials() combined with a wildcard origin - the CORS spec explicitly forbids "*" when credentials are allowed, and ASP.NET Core will throw or silently fail depending on version.
โ The Fix: Environment-Specific Origins + Verify Headers
- Read allowed origins from configuration per environment, never hardcode a single URL.
- After deploying, check the ACTUAL response headers with curl or browser DevTools’ Network tab — don’t just trust your Program.cs code, verify what’s really arriving at the browser.
โ๏ธ Environment-Aware CORS Policy
var allowedOrigins = builder.Configuration
.GetSection("Cors:AllowedOrigins").Get<string[]>() ?? Array.Empty<string>();
builder.Services.AddCors(options =>
{
options.AddPolicy("Default", policy =>
policy.WithOrigins(allowedOrigins)
.AllowAnyHeader()
.AllowAnyMethod());
});
// appsettings.Production.json:
// { "Cors": { "AllowedOrigins": ["https://app.example.com"] } }
๐ Verify What’s Actually Being Sent
curl -i -H "Origin: https://app.example.com" \
-X OPTIONS https://api.example.com/orders
# Check the response for Access-Control-Allow-Origin - if it's
# missing here, the proxy/server is the problem, not your CORS
# policy code.
A CORS error in the browser console always names the origin it blocked — read that message literally, because the fix is almost always exactly what it says: an origin your policy doesn’t list.
