Skip to main content

.NET CORS Configuration

Introduction

Cross-Origin Resource Sharing (CORS) is a security feature implemented by web browsers that restricts web pages from making requests to a different domain than the one that served the original page. As a developer building APIs or web services with .NET, understanding and properly configuring CORS is essential for creating secure applications that can still communicate with trusted external clients.

In this guide, we'll explore what CORS is, why it's important, and how to properly configure it in your .NET applications to balance security with functionality.

What is CORS?

CORS is a security mechanism built into modern browsers that follows the "same-origin policy." This policy prevents JavaScript code from making requests to a different origin (domain, protocol, or port) than the one from which it was served.

For example, if your frontend application hosted at https://myapp.com tries to make an API request to https://api.myapp.com, the browser will block this request unless the API server explicitly permits it through CORS headers.

Why is CORS Important?

CORS provides several important security benefits:

  1. Protection against Cross-Site Request Forgery (CSRF): It helps prevent malicious websites from making unauthorized requests to your API on behalf of authenticated users.

  2. Data Protection: It ensures that sensitive data from your API can only be accessed by approved web applications.

  3. Controlled Access: It allows you to explicitly define which external domains can interact with your API.

CORS in .NET Applications

.NET provides built-in middleware to handle CORS configuration easily. Let's see how to implement it in different .NET project types.

Basic CORS Configuration in ASP.NET Core

Here's how to set up basic CORS in an ASP.NET Core application:

csharp
// In Program.cs
var builder = WebApplication.CreateBuilder(args);

// Add CORS services
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyAllowSpecificOrigins",
policy =>
{
policy.WithOrigins("https://example.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});

// Other service configurations...

var app = builder.Build();

// Use CORS middleware
app.UseCors("MyAllowSpecificOrigins");

// Other middleware configurations...

app.Run();

This code:

  1. Adds CORS services to the dependency injection container
  2. Creates a named policy "MyAllowSpecificOrigins"
  3. Configures the policy to allow requests from the origin "https://example.com"
  4. Enables any HTTP headers and methods
  5. Applies the CORS middleware to the request pipeline

Allowing Multiple Origins

If your API needs to serve multiple client applications, you can configure multiple origins:

csharp
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "MyAllowSpecificOrigins",
policy =>
{
policy.WithOrigins(
"https://example.com",
"https://app.example.com",
"https://dev-app.example.com"
)
.AllowAnyHeader()
.AllowAnyMethod();
});
});

During development, you might want to temporarily allow all origins, but this should never be used in production:

csharp
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "DevelopmentPolicy",
policy =>
{
policy.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});

Configuring Specific Headers and Methods

For more restrictive policies, you can specify exactly which HTTP headers and methods are allowed:

csharp
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "RestrictivePolicy",
policy =>
{
policy.WithOrigins("https://example.com")
.WithHeaders("Content-Type", "Authorization")
.WithMethods("GET", "POST");
});
});

Allowing Credentials

If your API requires cookies, authentication headers, or client-side SSL certificates, you need to enable credentials:

csharp
builder.Services.AddCors(options =>
{
options.AddPolicy(name: "AllowCredentials",
policy =>
{
policy.WithOrigins("https://example.com")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});

Note: When using AllowCredentials(), you cannot use AllowAnyOrigin() or wildcard origins like *. You must specify exact origins.

Applying CORS to Specific Controllers or Actions

Instead of applying CORS globally, you can also apply it to specific controllers or actions:

csharp
// Apply to a controller
[EnableCors("MyAllowSpecificOrigins")]
public class ProductsController : ControllerBase
{
// Controller methods
}

// Apply to a specific action
public class CustomersController : ControllerBase
{
[EnableCors("RestrictivePolicy")]
[HttpGet]
public IEnumerable<Customer> Get()
{
// Method implementation
}
}

To disable CORS for a specific action within a controller that has CORS enabled:

csharp
[EnableCors("MyAllowSpecificOrigins")]
public class AnalyticsController : ControllerBase
{
[DisableCors]
[HttpGet("internal-stats")]
public ActionResult GetInternalStats()
{
// This action will not use CORS
}
}

Real-World Example: API with Different Policies

Let's implement a more comprehensive example showing how to use different CORS policies for different types of endpoints:

csharp
// In Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
// Public API endpoints - allow specific origins
options.AddPolicy("PublicApi", policy =>
{
policy.WithOrigins(
"https://public-client.com",
"https://www.public-client.com"
)
.WithMethods("GET")
.WithHeaders("Content-Type");
});

// Partner API endpoints - more permissive
options.AddPolicy("PartnerApi", policy =>
{
policy.WithOrigins("https://partner.com")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});

// Internal API endpoints - most permissive but still secure
options.AddPolicy("InternalApi", policy =>
{
policy.WithOrigins("https://admin.mycompany.com")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
.SetIsOriginAllowedToAllowWildcardSubdomains(); // Allow *.admin.mycompany.com
});
});

// Add controllers and other services
builder.Services.AddControllers();

var app = builder.Build();

// Use routing and authorization before CORS
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

// Add CORS middleware
app.UseCors(); // This enables CORS with the policies configured above

app.MapControllers();

app.Run();

And then in your controllers:

csharp
[EnableCors("PublicApi")]
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
[HttpGet]
public IActionResult GetProducts()
{
// Return public product data
}
}

[EnableCors("PartnerApi")]
[Route("api/partner/[controller]")]
[ApiController]
public class PartnerController : ControllerBase
{
[HttpPost("orders")]
public IActionResult SubmitOrder()
{
// Handle partner orders
}
}

[EnableCors("InternalApi")]
[Route("api/admin/[controller]")]
[ApiController]
[Authorize(Roles = "Admin")]
public class AdminController : ControllerBase
{
[HttpGet("analytics")]
public IActionResult GetAnalytics()
{
// Return sensitive analytics data
}
}

Common CORS Issues and Solutions

1. CORS errors despite configuration

If you're still seeing CORS errors even after configuring policies, check:

  • That the domain has the exact same spelling, including http/https and www/non-www
  • That the CORS middleware is added in the correct order (before endpoints but after routing)
  • That you're not overriding CORS headers elsewhere in your application

2. Preflight requests failing

For non-simple requests (like those using PUT/DELETE methods or custom headers), browsers send a "preflight" OPTIONS request first:

csharp
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowPreflightRequests",
policy =>
{
policy.WithOrigins("https://example.com")
.AllowAnyHeader()
.AllowAnyMethod()
// Handle preflight requests expiring after 24 hours
.SetPreflightMaxAge(TimeSpan.FromHours(24));
});
});

3. Issues with cookies or authentication

If you need to send cookies or authorization headers cross-origin:

csharp
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowCredentials",
policy =>
{
policy.WithOrigins("https://example.com")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});

And in your JavaScript client:

javascript
fetch('https://api.example.com/data', {
credentials: 'include', // This sends cookies
headers: {
'Authorization': 'Bearer your-token'
}
})

Best Practices for CORS in .NET

  1. Be as restrictive as possible: Only allow the specific origins, methods, and headers that your application needs.

  2. Never use AllowAnyOrigin() in production: Always specify the exact domains that should have access.

  3. Use environment-specific configurations: Have different CORS policies for development and production.

csharp
if (app.Environment.IsDevelopment())
{
app.UseCors("DevelopmentPolicy");
}
else
{
app.UseCors("ProductionPolicy");
}
  1. Regularly audit your CORS policies: Remove origins that no longer need access.

  2. Consider using a dynamic CORS policy: For larger applications, store allowed origins in a database or configuration service.

csharp
builder.Services.AddCors(options =>
{
options.AddPolicy("DynamicPolicy", policy =>
{
policy.SetIsOriginAllowed(origin =>
{
// Get allowed origins from configuration or database
var allowedOrigins = builder.Configuration.GetSection("AllowedOrigins").Get<string[]>();
return allowedOrigins.Contains(origin);
})
.AllowAnyMethod()
.AllowAnyHeader();
});
});

Summary

In this guide, we've explored:

  • What CORS is and why it's important for web application security
  • How to configure basic CORS policies in .NET applications
  • How to apply different policies to different controllers or actions
  • How to handle common CORS issues and implement best practices

CORS is a crucial security mechanism that helps protect your APIs and their users. By properly configuring CORS in your .NET applications, you can ensure that your APIs are accessible only to trusted clients, while maintaining strong security barriers against potential attacks.

Additional Resources

Practice Exercises

  1. Create a .NET Web API project and configure CORS to allow requests only from a specific domain.
  2. Extend your implementation to have different CORS policies for different controllers.
  3. Implement a dynamic CORS policy that reads allowed origins from appsettings.json.
  4. Set up a front-end application on a different domain and test your CORS configuration.
  5. Create a CORS policy that allows credentials and test it with a request that includes cookies.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)