// Security · 07
CORS
Tells the browser which other websites may read your API responses.
How it fits together
- Shop page uses / calls Orders API (1 preflight)
- Orders API publishes / notifies Shop page (2 allow origin)
- Shop page uses / calls Orders API (3 real request)
- Other site fails / error path Orders API (4 read blocked)
Spot it
- Console says "blocked by CORS policy"
- Works in curl, fails in the browser
- Preflight OPTIONS returns 401 or 405
It costs
Preflight adds a request, cut by max-age; a list that is too tight breaks real clients.
In code
var builder = WebApplication.CreateBuilder(args);builder.Services.AddCors(options =>{ options.AddPolicy("storefront", policy => { policy .WithOrigins("https://shop.example.com") .WithMethods("GET", "POST", "PUT") .WithHeaders("Content-Type", "Authorization") .AllowCredentials() .SetPreflightMaxAge(TimeSpan.FromMinutes(10)); });});var app = builder.Build();app.UseCors("storefront");Go deeperThe full explanation and what to ask for · 1 min readExpandCollapse
CORS relaxes the same-origin policy, and the browser enforces it. A page on one origin (scheme, host and port) that fetches from another origin may read the response only if that server says so with Access-Control-Allow-Origin. The server still runs the request; the browser just hides the result from the page. That is why curl ignores CORS entirely.
Non-simple requests (JSON bodies, custom headers, PUT or DELETE) first send a preflight OPTIONS asking permission. Answer it with the exact allowed origin, methods and headers, plus a max-age so browsers cache the answer. Credentialed requests need Access-Control-Allow-Credentials: true, and then the origin must be one exact value; browsers refuse a wildcard in that case.
CORS is not a security control for your API. Authentication and authorization must still be enforced on every endpoint, because non-browser clients ignore CORS, and simple cross-site requests are sent anyway. Keep the origin list short and explicit, and never echo the request Origin header back without checking it against that list. When NOT to bother: a public API with no cookies and no browser callers needs no CORS policy at all.
Ask for it
- same-origin policy
- origin (scheme, host, port)
- preflight (OPTIONS)
- simple vs non-simple request
- Access-Control-Allow-Origin
- credentialed request
Let only https://shop.example.com call /api/* from the browser. Answer preflight OPTIONS with that exact origin, methods GET, POST, PUT, headers Content-Type and Authorization, max-age 600s. Allow credentials for that origin only, never a wildcard. Keep server-side authorization unchanged.