Named, Default & Endpoint-Level CORS configuration for ASP.NET Core Web API β with security best practices, preflight handling, and anti-pattern examples.
If this sample saved you time, consider joining our Patreon community. You'll get exclusive .NET tutorials, premium code samples, and early access to new content β all for the price of a coffee.
π Join CodingDroplets on Patreon
Prefer a one-time tip? Buy us a coffee β
- How to define named CORS policies (
AddCors+options.AddPolicy) in ASP.NET Core - How to apply a global default policy via
app.UseCors("PolicyName") - How to override per-controller or per-action with
[EnableCors("PolicyName")] - How to opt out of CORS entirely with
[DisableCors](and when you'd ever want to) - The difference between
AllowAnyOrigin()andWithOrigins(...)β and why it matters with credentials - How preflight OPTIONS requests are handled automatically by the middleware
- Security best practices: principle of least privilege for API origins, headers, and methods
- Common CORS anti-patterns and how to avoid them
Browser Request
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β CORS Middleware β
β ββββββββββββββββββββββββββββββββββββ β
β β Evaluate against active policy β β
β β (global default or [EnableCors])β β
β ββββββββββββββββββββββββββββββββββββ β
β β’ Preflight (OPTIONS) handled here β
β β’ Access-Control-* headers added β
βββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
HTTP Router
β
βΌ
Controller Action
β [EnableCors("PublicApiPolicy")] β PublicApiPolicy
β (no attribute β inherits default) β FrontendPolicy
β [EnableCors("AdminPolicy")] β AdminPolicy
β [DisableCors] β No CORS headers
β
βΌ
Response
β
βΌ
Browser (respects Access-Control-Allow-Origin header)
| Policy | Origins | Credentials | Headers | Methods | Use Case |
|---|---|---|---|---|---|
PublicApiPolicy |
Any (*) |
β Not allowed | Any | Any | Public read-only endpoints |
FrontendPolicy (default) |
app.example.com, localhost:3000 |
β Allowed | Any | Any | Authenticated SPA clients |
AdminPolicy |
admin.example.com |
β Not set | Content-Type, Authorization |
GET, POST, PUT, DELETE | Admin back-office (strict) |
Key rule:
AllowAnyOrigin()andAllowCredentials()are mutually exclusive. ASP.NET Core will throw at startup if you combine them β by design, since a wildcard origin with credentials is a security vulnerability.
aspnet-core-cors-policy-demo/
βββ aspnet-core-cors-policy-demo.sln
βββ CorsDemo/
βββ CorsDemo.csproj # Project file β Scalar.AspNetCore package
βββ Program.cs # CORS policy definitions + middleware pipeline
βββ appsettings.json # App configuration
βββ Controllers/
β βββ PublicController.cs # [EnableCors("PublicApiPolicy")] override
β βββ ProductsController.cs # Uses global FrontendPolicy (no attribute)
β βββ AdminController.cs # [EnableCors("AdminPolicy")] + [DisableCors] demo
βββ Properties/
βββ launchSettings.json # Scalar UI auto-launch on F5
| Requirement | Version |
|---|---|
| .NET SDK | 10.0+ |
| IDE | Visual Studio 2022 v17.x / VS Code / Rider |
# Clone the repository
git clone https://github.com/codingdroplets/aspnet-core-cors-policy-demo.git
cd aspnet-core-cors-policy-demo
# Restore and build
dotnet build -c Release
# Run the API
cd CorsDemo
dotnet runOpen your browser to http://localhost:5289/scalar/v1 β the Scalar API explorer opens automatically.
builder.Services.AddCors(options =>
{
// Policy 1: Public endpoints β any origin, no credentials
options.AddPolicy("PublicApiPolicy", policy =>
policy
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
// Policy 2: Authenticated SPA β explicit origins + credentials
options.AddPolicy("FrontendPolicy", policy =>
policy
.WithOrigins("https://app.example.com", "http://localhost:3000")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials());
// Policy 3: Admin back-office β maximum restriction
options.AddPolicy("AdminPolicy", policy =>
policy
.WithOrigins("https://admin.example.com")
.WithHeaders("Content-Type", "Authorization")
.WithMethods("GET", "POST", "PUT", "DELETE"));
});// Must come before UseAuthorization() and MapControllers()
app.UseCors("FrontendPolicy");[ApiController]
[Route("api/public")]
[EnableCors("PublicApiPolicy")] // overrides FrontendPolicy for this entire controller
public class PublicController : ControllerBase { ... }[ApiController]
[Route("api/admin")]
[EnableCors("AdminPolicy")] // controller-wide override
public class AdminController : ControllerBase
{
[HttpGet("health")]
[DisableCors] // no CORS headers on this action β cross-origin blocked intentionally
public IActionResult GetHealth() { ... }
}ASP.NET Core CORS middleware handles OPTIONS preflight requests automatically. No controller action is needed β the middleware intercepts and responds with the appropriate Access-Control-* headers before the request reaches your controller.
| Method | Endpoint | CORS Policy | Description |
|---|---|---|---|
GET |
/api/public |
PublicApiPolicy (any origin) |
Public announcements β no auth required |
GET |
/api/products |
FrontendPolicy (default) |
List all products |
GET |
/api/products/{id} |
FrontendPolicy (default) |
Get product by ID |
POST |
/api/products |
FrontendPolicy (default) |
Create a new product |
GET |
/api/admin |
AdminPolicy (admin.example.com only) |
List admin users |
DELETE |
/api/admin/{id} |
AdminPolicy (admin.example.com only) |
Delete an admin user |
GET |
/api/admin/health |
None [DisableCors] |
Health check β cross-origin blocked |
GET |
/scalar/v1 |
N/A | Scalar API explorer (development only) |
GET |
/openapi/v1.json |
N/A | OpenAPI JSON document |
This project is focused on demonstrating CORS middleware configuration. You can test all CORS behaviours directly via the Scalar UI or with curl:
# Test PublicApiPolicy β any origin works
curl -H "Origin: https://random.example.com" \
-H "Access-Control-Request-Method: GET" \
-X OPTIONS http://localhost:5289/api/public -v
# Test FrontendPolicy β allowed origin
curl -H "Origin: http://localhost:3000" \
-H "Access-Control-Request-Method: GET" \
-X OPTIONS http://localhost:5289/api/products -v
# Test AdminPolicy β disallowed origin (expect no CORS headers in response)
curl -H "Origin: https://attacker.com" \
-H "Access-Control-Request-Method: GET" \
-X OPTIONS http://localhost:5289/api/admin -v
# Test [DisableCors] β no CORS headers regardless of origin
curl -H "Origin: https://admin.example.com" \
-H "Access-Control-Request-Method: GET" \
-X OPTIONS http://localhost:5289/api/admin/health -v| Approach | How to Apply | Scope | Best For |
|---|---|---|---|
| Default policy | app.UseCors("Name") |
Entire app | Single-policy APIs |
| Named policy | [EnableCors("Name")] |
Controller or action | Mixed-policy APIs |
| Disable CORS | [DisableCors] |
Specific action | Internal diagnostic routes |
// β
Safe for public, unauthenticated data
policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()
// β
Required when credentials (cookies/auth headers) are involved
policy.WithOrigins("https://app.example.com").AllowCredentials()
// β INVALID β throws InvalidOperationException at runtime
policy.AllowAnyOrigin().AllowCredentials()app.UseRouting();
app.UseCors("MyPolicy"); // β must be AFTER UseRouting, BEFORE UseAuthorization
app.UseAuthorization();
app.MapControllers();CORS headers tell the browser whether to allow the response. A malicious server-to-server call can always ignore CORS. Use authentication and authorisation to secure your API β CORS alone is not a security boundary.
- ASP.NET Core 10 β Web API framework
- Microsoft.AspNetCore.Cors β Built-in CORS middleware (no extra package needed)
- Scalar.AspNetCore β Beautiful OpenAPI/Scalar UI replacing Swagger UI
- System.Text.Json β Built-in JSON serialisation with
ReferenceHandler.IgnoreCycles - .NET 10 SDK β Target framework
- Enable Cross-Origin Requests (CORS) in ASP.NET Core β Microsoft Docs
- Cross-Origin Resource Sharing (CORS) β MDN Web Docs
- Fetch Standard β CORS Protocol (WHATWG)
- π Deep Dive: ASP.NET Core CORS Policy Enterprise Decision Guide
This project is licensed under the MIT License.
| Platform | Link |
|---|---|
| π Website | https://codingdroplets.com/ |
| πΊ YouTube | https://www.youtube.com/@CodingDroplets |
| π Patreon | https://www.patreon.com/CodingDroplets |
| β Buy Me a Coffee | https://buymeacoffee.com/codingdroplets |
| π» GitHub | http://github.com/codingdroplets/ |
Want more samples like this? Support us on Patreon or buy us a coffee β β every bit helps keep the content coming!