No articles found
Try different keywords or browse our categories
Fix: CORS Error in ASP.NET Core - Complete Cross-Origin Guide
Learn how to fix CORS errors in ASP.NET Core applications. This comprehensive guide covers CORS configuration, policy setup, and proper cross-origin request handling techniques.
The ‘CORS Error’ is a common issue in ASP.NET Core applications that occurs when a web page attempts to make requests to a different domain than the one that served the web page. This error is triggered by browsers’ same-origin policy, which restricts cross-origin requests for security reasons. Understanding and resolving CORS errors is crucial for building modern web applications that need to communicate across different domains.
This comprehensive guide explains what causes CORS errors, why they happen, and provides multiple solutions to fix and prevent them in your ASP.NET Core projects with clean code examples and directory structure.
What is the CORS Error?
CORS (Cross-Origin Resource Sharing) errors occur when:
- A web page makes a request to a different origin (domain, protocol, or port)
- The server doesn’t allow cross-origin requests
- CORS headers are missing or incorrectly configured
- Request methods are not permitted
- Request headers are not allowed
- Credentials are not properly handled
- Preflight requests fail
Common Error Messages:
Access to fetch at 'https://api.example.com/data' from origin 'https://myapp.com' has been blocked by CORS policyNo 'Access-Control-Allow-Origin' header is present on the requested resourceThe value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'CORS policy has been disabled for this requestRequest header field content-type is not allowed by Access-Control-Allow-Headers
Understanding the Problem
CORS is a security feature implemented by web browsers to prevent malicious websites from making unauthorized requests to other domains on behalf of the user. When a browser detects that a request is being made to a different origin than the current page, it sends a preflight request to check if the server allows the cross-origin request. If the server doesn’t respond with appropriate CORS headers, the browser blocks the request.
Typical ASP.NET Core Project Structure:
MyCorsApp/
├── MyCorsApp.sln
├── src/
│ ├── MyCorsApp/
│ │ ├── Program.cs
│ │ ├── Startup.cs
│ │ ├── Controllers/
│ │ │ ├── HomeController.cs
│ │ │ └── ApiController.cs
│ │ ├── Models/
│ │ │ ├── User.cs
│ │ │ └── ApiResponse.cs
│ │ ├── Services/
│ │ │ ├── ICorsService.cs
│ │ │ └── CorsService.cs
│ │ ├── MyCorsApp.csproj
│ │ └── appsettings.json
│ └── MyCorsApp.Tests/
│ ├── UnitTests.cs
│ └── MyCorsApp.Tests.csproj
├── packages/
└── bin/
Solution 1: Proper CORS Configuration
The most fundamental approach to prevent CORS errors is to configure CORS properly in your ASP.NET Core application.
❌ Without Proper CORS Configuration:
// Program.cs - ❌ No CORS configuration
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
// ❌ Missing CORS configuration
builder.Services.AddControllers();
var app = builder.Build();
// ❌ No CORS middleware added
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
app.Run();
✅ With Proper CORS Configuration:
Models/User.cs:
using System.ComponentModel.DataAnnotations;
namespace MyCorsApp.Models
{
public class User
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; } = string.Empty;
[Required]
[EmailAddress]
[StringLength(255)]
public string Email { get; set; } = string.Empty;
public bool IsActive { get; set; } = true;
public string? Role { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? UpdatedAt { get; set; }
}
}
Models/ApiResponse.cs:
namespace MyCorsApp.Models
{
public class ApiResponse<T>
{
public bool Success { get; set; }
public string Message { get; set; } = string.Empty;
public T? Data { get; set; }
public List<string> Errors { get; set; } = new List<string>();
}
public class ApiResponse
{
public bool Success { get; set; }
public string Message { get; set; } = string.Empty;
public List<string> Errors { get; set; } = new List<string>();
}
}
Services/ICorsService.cs:
using MyCorsApp.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MyCorsApp.Services
{
public interface ICorsService
{
Task<List<User>> GetUsersAsync();
Task<User> GetUserByIdAsync(int id);
Task<User> CreateUserAsync(User user);
Task<User> UpdateUserAsync(int id, User user);
Task<bool> DeleteUserAsync(int id);
}
}
Services/CorsService.cs:
using MyCorsApp.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MyCorsApp.Services
{
public class CorsService : ICorsService
{
private readonly List<User> _users;
public CorsService()
{
_users = new List<User>
{
new User { Id = 1, Name = "John Doe", Email = "john@example.com", IsActive = true, Role = "Admin" },
new User { Id = 2, Name = "Jane Smith", Email = "jane@example.com", IsActive = true, Role = "User" },
new User { Id = 3, Name = "Bob Johnson", Email = "bob@example.com", IsActive = false, Role = "User" }
};
}
public async Task<List<User>> GetUsersAsync()
{
return await Task.FromResult(_users.ToList());
}
public async Task<User> GetUserByIdAsync(int id)
{
var user = _users.FirstOrDefault(u => u.Id == id);
return await Task.FromResult(user);
}
public async Task<User> CreateUserAsync(User user)
{
user.Id = _users.Any() ? _users.Max(u => u.Id) + 1 : 1;
user.CreatedAt = DateTime.UtcNow;
_users.Add(user);
return await Task.FromResult(user);
}
public async Task<User> UpdateUserAsync(int id, User user)
{
var existingUser = _users.FirstOrDefault(u => u.Id == id);
if (existingUser == null)
{
return null;
}
existingUser.Name = user.Name;
existingUser.Email = user.Email;
existingUser.IsActive = user.IsActive;
existingUser.Role = user.Role;
existingUser.UpdatedAt = DateTime.UtcNow;
return await Task.FromResult(existingUser);
}
public async Task<bool> DeleteUserAsync(int id)
{
var user = _users.FirstOrDefault(u => u.Id == id);
if (user == null)
{
return false;
}
_users.Remove(user);
return await Task.FromResult(true);
}
// ✅ Additional service methods
public async Task<List<User>> GetActiveUsersAsync()
{
var activeUsers = _users.Where(u => u.IsActive).ToList();
return await Task.FromResult(activeUsers);
}
public async Task<int> GetUserCountAsync()
{
return await Task.FromResult(_users.Count);
}
public async Task<User> GetUserByEmailAsync(string email)
{
var user = _users.FirstOrDefault(u =>
u.Email.Equals(email, StringComparison.OrdinalIgnoreCase));
return await Task.FromResult(user);
}
}
}
Controllers/ApiController.cs:
using Microsoft.AspNetCore.Mvc;
using MyCorsApp.Models;
using MyCorsApp.Services;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MyCorsApp.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class ApiController : ControllerBase
{
private readonly ICorsService _corsService;
public ApiController(ICorsService corsService)
{
_corsService = corsService;
}
[HttpGet("users")]
public async Task<ActionResult<ApiResponse<List<User>>>> GetUsers()
{
try
{
var users = await _corsService.GetUsersAsync();
var response = new ApiResponse<List<User>>
{
Success = true,
Message = "Users retrieved successfully",
Data = users
};
return Ok(response);
}
catch (Exception ex)
{
var response = new ApiResponse<List<User>>
{
Success = false,
Message = "Error retrieving users",
Errors = new List<string> { ex.Message }
};
return StatusCode(500, response);
}
}
[HttpGet("users/{id}")]
public async Task<ActionResult<ApiResponse<User>>> GetUser(int id)
{
try
{
if (id <= 0)
{
var errorResponse = new ApiResponse<User>
{
Success = false,
Message = "Invalid user ID",
Errors = new List<string> { "User ID must be greater than 0" }
};
return BadRequest(errorResponse);
}
var user = await _corsService.GetUserByIdAsync(id);
if (user == null)
{
var errorResponse = new ApiResponse<User>
{
Success = false,
Message = $"User with ID {id} not found",
Errors = new List<string> { "User not found" }
};
return NotFound(errorResponse);
}
var response = new ApiResponse<User>
{
Success = true,
Message = "User retrieved successfully",
Data = user
};
return Ok(response);
}
catch (Exception ex)
{
var response = new ApiResponse<User>
{
Success = false,
Message = "Error retrieving user",
Errors = new List<string> { ex.Message }
};
return StatusCode(500, response);
}
}
[HttpPost("users")]
public async Task<ActionResult<ApiResponse<User>>> CreateUser([FromBody] User user)
{
try
{
if (user == null)
{
var errorResponse = new ApiResponse<User>
{
Success = false,
Message = "User data is required",
Errors = new List<string> { "User object cannot be null" }
};
return BadRequest(errorResponse);
}
if (string.IsNullOrWhiteSpace(user.Name) || string.IsNullOrWhiteSpace(user.Email))
{
var errorResponse = new ApiResponse<User>
{
Success = false,
Message = "User name and email are required",
Errors = new List<string> { "Name and email are required fields" }
};
return BadRequest(errorResponse);
}
var createdUser = await _corsService.CreateUserAsync(user);
var response = new ApiResponse<User>
{
Success = true,
Message = "User created successfully",
Data = createdUser
};
return CreatedAtAction(nameof(GetUser), new { id = createdUser.Id }, response);
}
catch (Exception ex)
{
var response = new ApiResponse<User>
{
Success = false,
Message = "Error creating user",
Errors = new List<string> { ex.Message }
};
return StatusCode(500, response);
}
}
[HttpPut("users/{id}")]
public async Task<ActionResult<ApiResponse<User>>> UpdateUser(int id, [FromBody] User user)
{
try
{
if (id <= 0 || user == null)
{
var errorResponse = new ApiResponse<User>
{
Success = false,
Message = "Invalid user ID or data",
Errors = new List<string> { "User ID must be greater than 0 and user data cannot be null" }
};
return BadRequest(errorResponse);
}
if (id != user.Id)
{
var errorResponse = new ApiResponse<User>
{
Success = false,
Message = "User ID mismatch",
Errors = new List<string> { "ID in URL must match ID in request body" }
};
return BadRequest(errorResponse);
}
var updatedUser = await _corsService.UpdateUserAsync(id, user);
if (updatedUser == null)
{
var errorResponse = new ApiResponse<User>
{
Success = false,
Message = $"User with ID {id} not found",
Errors = new List<string> { "User not found" }
};
return NotFound(errorResponse);
}
var response = new ApiResponse<User>
{
Success = true,
Message = "User updated successfully",
Data = updatedUser
};
return Ok(response);
}
catch (Exception ex)
{
var response = new ApiResponse<User>
{
Success = false,
Message = "Error updating user",
Errors = new List<string> { ex.Message }
};
return StatusCode(500, response);
}
}
[HttpDelete("users/{id}")]
public async Task<ActionResult<ApiResponse>> DeleteUser(int id)
{
try
{
if (id <= 0)
{
var errorResponse = new ApiResponse
{
Success = false,
Message = "Invalid user ID",
Errors = new List<string> { "User ID must be greater than 0" }
};
return BadRequest(errorResponse);
}
var success = await _corsService.DeleteUserAsync(id);
if (!success)
{
var errorResponse = new ApiResponse
{
Success = false,
Message = $"User with ID {id} not found",
Errors = new List<string> { "User not found" }
};
return NotFound(errorResponse);
}
var response = new ApiResponse
{
Success = true,
Message = "User deleted successfully"
};
return Ok(response);
}
catch (Exception ex)
{
var response = new ApiResponse
{
Success = false,
Message = "Error deleting user",
Errors = new List<string> { ex.Message }
};
return StatusCode(500, response);
}
}
[HttpGet("users/active")]
public async Task<ActionResult<ApiResponse<List<User>>>> GetActiveUsers()
{
try
{
var users = await _corsService.GetActiveUsersAsync();
var response = new ApiResponse<List<User>>
{
Success = true,
Message = "Active users retrieved successfully",
Data = users
};
return Ok(response);
}
catch (Exception ex)
{
var response = new ApiResponse<List<User>>
{
Success = false,
Message = "Error retrieving active users",
Errors = new List<string> { ex.Message }
};
return StatusCode(500, response);
}
}
[HttpGet("users/count")]
public async Task<ActionResult<ApiResponse<int>>> GetUserCount()
{
try
{
var count = await _corsService.GetUserCountAsync();
var response = new ApiResponse<int>
{
Success = true,
Message = "User count retrieved successfully",
Data = count
};
return Ok(response);
}
catch (Exception ex)
{
var response = new ApiResponse<int>
{
Success = false,
Message = "Error retrieving user count",
Errors = new List<string> { ex.Message }
};
return StatusCode(500, response);
}
}
}
}
Program.cs (for .NET 6+):
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MyCorsApp.Services;
var builder = WebApplication.CreateBuilder(args);
// ✅ Add services to the container
builder.Services.AddControllers();
// ✅ Configure CORS policies
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowSpecificOrigins", policy =>
{
policy.WithOrigins("https://localhost:3000", "https://localhost:4200", "https://myapp.com")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
options.AddPolicy("AllowAllOrigins", policy =>
{
policy.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
options.AddPolicy("CustomCorsPolicy", policy =>
{
policy.WithOrigins("https://trusted-domain.com", "https://another-trusted-domain.com")
.WithHeaders("Content-Type", "Authorization", "X-Requested-With")
.WithMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.AllowCredentials();
});
});
// ✅ Register services
builder.Services.AddScoped<ICorsService, CorsService>();
var app = builder.Build();
// ✅ Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
// ✅ Enable CORS with specific policy
app.UseCors("AllowSpecificOrigins");
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
app.Run();
Solution 2: Advanced CORS Configuration
Configure more sophisticated CORS policies for different scenarios.
Program.cs with Advanced CORS:
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MyCorsApp.Services;
var builder = WebApplication.CreateBuilder(args);
// ✅ Add services to the container
builder.Services.AddControllers();
// ✅ Configure advanced CORS policies
builder.Services.AddCors(options =>
{
// ✅ Development policy - allows multiple origins
options.AddPolicy("DevelopmentPolicy", policy =>
{
policy.WithOrigins(
"https://localhost:3000", // React dev server
"https://localhost:4200", // Angular dev server
"https://localhost:8080", // Vue dev server
"http://localhost:3000",
"http://localhost:4200",
"http://localhost:8080"
)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
// ✅ Production policy - specific trusted origins
options.AddPolicy("ProductionPolicy", policy =>
{
policy.WithOrigins(
"https://myapp.com",
"https://www.myapp.com",
"https://api.myapp.com"
)
.WithHeaders("Content-Type", "Authorization", "X-Requested-With", "Accept")
.WithMethods("GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS")
.AllowCredentials();
});
// ✅ API policy - for API consumers
options.AddPolicy("ApiPolicy", policy =>
{
policy.SetIsOriginAllowed(origin => new Uri(origin).Host.EndsWith(".mycompany.com"))
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
// ✅ Minimal policy - for public APIs
options.AddPolicy("PublicApiPolicy", policy =>
{
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
// ✅ Strict policy - for sensitive operations
options.AddPolicy("StrictPolicy", policy =>
{
policy.WithOrigins("https://secure.myapp.com")
.WithHeaders("Content-Type", "Authorization")
.WithMethods("GET", "POST")
.AllowCredentials();
});
});
// ✅ Register services
builder.Services.AddScoped<ICorsService, CorsService>();
var app = builder.Build();
// ✅ Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
// ✅ Use development CORS policy
app.UseCors("DevelopmentPolicy");
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
// ✅ Use production CORS policy
app.UseCors("ProductionPolicy");
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
app.Run();
Solution 3: Controller-Level CORS Configuration
Apply CORS policies at the controller or action level for fine-grained control.
Controllers/SpecializedController.cs:
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using MyCorsApp.Models;
using MyCorsApp.Services;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MyCorsApp.Controllers
{
[ApiController]
[Route("api/[controller]")]
// ✅ Apply CORS policy at controller level
[EnableCors("PublicApiPolicy")]
public class PublicApiController : ControllerBase
{
private readonly ICorsService _corsService;
public PublicApiController(ICorsService corsService)
{
_corsService = corsService;
}
[HttpGet("public-data")]
// ✅ This action uses the controller-level policy
public async Task<ActionResult<ApiResponse<List<User>>>> GetPublicData()
{
try
{
var users = await _corsService.GetUsersAsync();
var response = new ApiResponse<List<User>>
{
Success = true,
Message = "Public data retrieved successfully",
Data = users
};
return Ok(response);
}
catch (Exception ex)
{
var response = new ApiResponse<List<User>>
{
Success = false,
Message = "Error retrieving public data",
Errors = new List<string> { ex.Message }
};
return StatusCode(500, response);
}
}
[HttpGet("restricted-data")]
// ✅ Override with more restrictive policy
[EnableCors("StrictPolicy")]
public async Task<ActionResult<ApiResponse<List<User>>>> GetRestrictedData()
{
try
{
var users = await _corsService.GetActiveUsersAsync();
var response = new ApiResponse<List<User>>
{
Success = true,
Message = "Restricted data retrieved successfully",
Data = users
};
return Ok(response);
}
catch (Exception ex)
{
var response = new ApiResponse<List<User>>
{
Success = false,
Message = "Error retrieving restricted data",
Errors = new List<string> { ex.Message }
};
return StatusCode(500, response);
}
}
[HttpPost("submit-data")]
// ✅ Use the same restrictive policy for sensitive operations
[EnableCors("StrictPolicy")]
public async Task<ActionResult<ApiResponse<User>>> SubmitData([FromBody] User user)
{
try
{
if (user == null)
{
var errorResponse = new ApiResponse<User>
{
Success = false,
Message = "User data is required",
Errors = new List<string> { "User object cannot be null" }
};
return BadRequest(errorResponse);
}
var createdUser = await _corsService.CreateUserAsync(user);
var response = new ApiResponse<User>
{
Success = true,
Message = "Data submitted successfully",
Data = createdUser
};
return Ok(response);
}
catch (Exception ex)
{
var response = new ApiResponse<User>
{
Success = false,
Message = "Error submitting data",
Errors = new List<string> { ex.Message }
};
return StatusCode(500, response);
}
}
}
[ApiController]
[Route("api/[controller]")]
// ✅ Disable CORS for this controller if needed
[DisableCors]
public class InternalController : ControllerBase
{
private readonly ICorsService _corsService;
public InternalController(ICorsService corsService)
{
_corsService = corsService;
}
[HttpGet("internal-data")]
// ✅ This endpoint won't accept cross-origin requests
public async Task<ActionResult<ApiResponse<List<User>>>> GetInternalData()
{
try
{
var users = await _corsService.GetUsersAsync();
var response = new ApiResponse<List<User>>
{
Success = true,
Message = "Internal data retrieved successfully",
Data = users
};
return Ok(response);
}
catch (Exception ex)
{
var response = new ApiResponse<List<User>>
{
Success = false,
Message = "Error retrieving internal data",
Errors = new List<string> { ex.Message }
};
return StatusCode(500, response);
}
}
}
}
Solution 4: Dynamic CORS Policy
Create dynamic CORS policies based on request conditions.
Services/ICorsPolicyService.cs:
using Microsoft.AspNetCore.Cors.Infrastructure;
using System.Threading.Tasks;
namespace MyCorsApp.Services
{
public interface ICorsPolicyService
{
Task<CorsPolicy> GetCorsPolicyAsync(string origin);
Task<bool> IsOriginAllowedAsync(string origin);
}
}
Services/CorsPolicyService.cs:
using Microsoft.AspNetCore.Cors.Infrastructure;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MyCorsApp.Services
{
public class CorsPolicyService : ICorsPolicyService
{
private readonly HashSet<string> _allowedOrigins;
private readonly HashSet<string> _allowedHeaders;
private readonly HashSet<string> _allowedMethods;
public CorsPolicyService()
{
// ✅ Initialize with common allowed values
_allowedOrigins = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"https://localhost:3000",
"https://localhost:4200",
"https://localhost:8080",
"https://myapp.com",
"https://www.myapp.com"
};
_allowedHeaders = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"Content-Type",
"Authorization",
"X-Requested-With",
"Accept",
"Origin"
};
_allowedMethods = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"GET",
"POST",
"PUT",
"DELETE",
"PATCH",
"HEAD",
"OPTIONS"
};
}
public async Task<CorsPolicy> GetCorsPolicyAsync(string origin)
{
var policy = new CorsPolicy
{
AllowCredentials = true,
SupportsCredentials = true
};
// ✅ Add allowed origins
if (await IsOriginAllowedAsync(origin))
{
policy.Origins.Add(origin);
}
// ✅ Add allowed headers
foreach (var header in _allowedHeaders)
{
policy.Headers.Add(header);
}
// ✅ Add allowed methods
foreach (var method in _allowedMethods)
{
policy.Methods.Add(method);
}
// ✅ Set max age
policy.ExposedHeaders.Add("*");
policy.PreflightMaxAge = TimeSpan.FromHours(1);
return await Task.FromResult(policy);
}
public async Task<bool> IsOriginAllowedAsync(string origin)
{
if (string.IsNullOrEmpty(origin))
{
return await Task.FromResult(false);
}
// ✅ Check if origin is in allowed list
if (_allowedOrigins.Contains(origin))
{
return await Task.FromResult(true);
}
// ✅ Check for wildcard subdomains
var uri = new Uri(origin);
var host = uri.Host;
// ✅ Allow subdomains of trusted domains
var trustedDomains = new[] { "myapp.com", "mycompany.com" };
foreach (var domain in trustedDomains)
{
if (host.EndsWith($".{domain}") || host.Equals(domain, StringComparison.OrdinalIgnoreCase))
{
return await Task.FromResult(true);
}
}
return await Task.FromResult(false);
}
// ✅ Additional methods for managing allowed origins
public void AddAllowedOrigin(string origin)
{
_allowedOrigins.Add(origin);
}
public void RemoveAllowedOrigin(string origin)
{
_allowedOrigins.Remove(origin);
}
public void AddAllowedHeader(string header)
{
_allowedHeaders.Add(header);
}
public void AddAllowedMethod(string method)
{
_allowedMethods.Add(method);
}
public HashSet<string> GetAllowedOrigins()
{
return new HashSet<string>(_allowedOrigins);
}
}
}
Program.cs with Dynamic CORS:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MyCorsApp.Services;
var builder = WebApplication.CreateBuilder(args);
// ✅ Add services to the container
builder.Services.AddControllers();
// ✅ Add CORS services
builder.Services.AddCors();
// ✅ Register custom CORS policy service
builder.Services.AddScoped<ICorsPolicyService, CorsPolicyService>();
var app = builder.Build();
// ✅ Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
// ✅ Use dynamic CORS middleware
app.Use(async (context, next) =>
{
var corsPolicyService = context.RequestServices.GetRequiredService<ICorsPolicyService>();
var origin = context.Request.Headers["Origin"].FirstOrDefault();
if (!string.IsNullOrEmpty(origin))
{
var policy = await corsPolicyService.GetCorsPolicyAsync(origin);
if (policy != null)
{
var corsService = context.RequestServices.GetRequiredService<ICorsService>();
context.Response.Headers.Add("Access-Control-Allow-Origin", origin);
context.Response.Headers.Add("Access-Control-Allow-Methods", string.Join(",", policy.Methods));
context.Response.Headers.Add("Access-Control-Allow-Headers", string.Join(",", policy.Headers));
context.Response.Headers.Add("Access-Control-Allow-Credentials", "true");
}
}
await next();
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
app.Run();
Solution 5: Environment-Specific CORS Configuration
Configure CORS differently based on the environment.
Configuration/CorsConfiguration.cs:
using Microsoft.Extensions.Configuration;
namespace MyCorsApp.Configuration
{
public class CorsConfiguration
{
public string[] AllowedOrigins { get; set; } = Array.Empty<string>();
public string[] AllowedHeaders { get; set; } = Array.Empty<string>();
public string[] AllowedMethods { get; set; } = Array.Empty<string>();
public bool AllowCredentials { get; set; } = true;
public bool AllowAnyOrigin { get; set; } = false;
public bool AllowAnyHeader { get; set; } = false;
public bool AllowAnyMethod { get; set; } = false;
}
public static class CorsConfigurationExtensions
{
public static CorsConfiguration GetCorsConfiguration(this IConfiguration configuration)
{
var corsConfig = new CorsConfiguration();
configuration.GetSection("Cors").Bind(corsConfig);
return corsConfig;
}
}
}
appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Cors": {
"AllowedOrigins": [
"https://localhost:3000",
"https://localhost:4200",
"https://myapp.com"
],
"AllowedHeaders": [
"Content-Type",
"Authorization",
"X-Requested-With",
"Accept"
],
"AllowedMethods": [
"GET",
"POST",
"PUT",
"DELETE",
"OPTIONS"
],
"AllowCredentials": true
}
}
appsettings.Development.json:
{
"Cors": {
"AllowedOrigins": [
"https://localhost:3000",
"https://localhost:4200",
"https://localhost:8080",
"http://localhost:3000",
"http://localhost:4200",
"http://localhost:8080"
],
"AllowedHeaders": [
"Content-Type",
"Authorization",
"X-Requested-With",
"Accept",
"X-Custom-Header"
],
"AllowedMethods": [
"GET",
"POST",
"PUT",
"DELETE",
"PATCH",
"HEAD",
"OPTIONS"
],
"AllowCredentials": true,
"AllowAnyOrigin": false,
"AllowAnyHeader": false,
"AllowAnyMethod": false
}
}
appsettings.Production.json:
{
"Cors": {
"AllowedOrigins": [
"https://myapp.com",
"https://www.myapp.com",
"https://api.myapp.com"
],
"AllowedHeaders": [
"Content-Type",
"Authorization",
"X-Requested-With",
"Accept"
],
"AllowedMethods": [
"GET",
"POST",
"PUT",
"DELETE",
"OPTIONS"
],
"AllowCredentials": true,
"AllowAnyOrigin": false,
"AllowAnyHeader": false,
"AllowAnyMethod": false
}
}
Program.cs with Environment-Specific CORS:
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Configuration;
using MyCorsApp.Configuration;
using MyCorsApp.Services;
var builder = WebApplication.CreateBuilder(args);
// ✅ Add configuration
builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
builder.Configuration.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true);
builder.Configuration.AddEnvironmentVariables();
// ✅ Add services to the container
builder.Services.AddControllers();
// ✅ Get CORS configuration from settings
var corsConfig = builder.Configuration.GetCorsConfiguration();
// ✅ Configure CORS based on environment
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
if (corsConfig.AllowAnyOrigin)
{
policy.AllowAnyOrigin();
}
else
{
policy.WithOrigins(corsConfig.AllowedOrigins);
}
if (corsConfig.AllowAnyHeader)
{
policy.AllowAnyHeader();
}
else
{
policy.WithHeaders(corsConfig.AllowedHeaders);
}
if (corsConfig.AllowAnyMethod)
{
policy.AllowAnyMethod();
}
else
{
policy.WithMethods(corsConfig.AllowedMethods);
}
if (corsConfig.AllowCredentials)
{
policy.AllowCredentials();
}
});
// ✅ Add specific named policy
options.AddPolicy("EnvironmentPolicy", policy =>
{
if (corsConfig.AllowAnyOrigin)
{
policy.AllowAnyOrigin();
}
else
{
policy.WithOrigins(corsConfig.AllowedOrigins);
}
if (corsConfig.AllowAnyHeader)
{
policy.AllowAnyHeader();
}
else
{
policy.WithHeaders(corsConfig.AllowedHeaders);
}
if (corsConfig.AllowAnyMethod)
{
policy.AllowAnyMethod();
}
else
{
policy.WithMethods(corsConfig.AllowedMethods);
}
if (corsConfig.AllowCredentials)
{
policy.AllowCredentials();
}
});
});
// ✅ Register services
builder.Services.AddScoped<ICorsService, CorsService>();
var app = builder.Build();
// ✅ Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
// ✅ Use environment-specific CORS policy
app.UseCors("EnvironmentPolicy");
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
app.Run();
Working Code Examples
Complete ASP.NET Core Application with Proper CORS Configuration:
// Program.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Configuration;
using MyCorsApp.Configuration;
using MyCorsApp.Services;
var builder = WebApplication.CreateBuilder(args);
// ✅ Add configuration
builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
builder.Configuration.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true);
builder.Configuration.AddEnvironmentVariables();
// ✅ Add services to the container
builder.Services.AddControllers();
// ✅ Get CORS configuration
var corsConfig = builder.Configuration.GetCorsConfiguration();
// ✅ Configure CORS
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
if (corsConfig.AllowAnyOrigin)
{
policy.AllowAnyOrigin();
}
else
{
policy.WithOrigins(corsConfig.AllowedOrigins);
}
if (corsConfig.AllowAnyHeader)
{
policy.AllowAnyHeader();
}
else
{
policy.WithHeaders(corsConfig.AllowedHeaders);
}
if (corsConfig.AllowAnyMethod)
{
policy.AllowAnyMethod();
}
else
{
policy.WithMethods(corsConfig.AllowedMethods);
}
if (corsConfig.AllowCredentials)
{
policy.AllowCredentials();
}
});
});
// ✅ Register services
builder.Services.AddScoped<ICorsService, CorsService>();
var app = builder.Build();
// ✅ Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
// ✅ Enable CORS
app.UseCors();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
app.Run();
Unit Test Example:
// MyCorsApp.Tests/UnitTests.cs
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MyCorsApp.Services;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
namespace MyCorsApp.Tests
{
[TestClass]
public class CorsTests
{
private WebApplicationFactory<Program> _factory;
private HttpClient _httpClient;
[TestInitialize]
public void Setup()
{
_factory = new WebApplicationFactory<Program>();
_httpClient = _factory.CreateClient();
}
[TestMethod]
public async Task CorsService_GetUsers_ReturnsSuccess()
{
// ✅ Arrange
var corsService = new CorsService();
// ✅ Act
var result = await corsService.GetUsersAsync();
// ✅ Assert
Assert.IsNotNull(result);
Assert.IsTrue(result.Count > 0);
}
[TestMethod]
public async Task CorsPolicyService_IsOriginAllowed_ValidOrigin_ReturnsTrue()
{
// ✅ Arrange
var corsPolicyService = new CorsPolicyService();
var validOrigin = "https://localhost:3000";
// ✅ Act
var result = await corsPolicyService.IsOriginAllowedAsync(validOrigin);
// ✅ Assert
Assert.IsTrue(result);
}
[TestMethod]
public async Task CorsPolicyService_IsOriginAllowed_InvalidOrigin_ReturnsFalse()
{
// ✅ Arrange
var corsPolicyService = new CorsPolicyService();
var invalidOrigin = "https://malicious-site.com";
// ✅ Act
var result = await corsPolicyService.IsOriginAllowedAsync(invalidOrigin);
// ✅ Assert
Assert.IsFalse(result);
}
[TestMethod]
public async Task CorsPolicyService_GetCorsPolicy_ValidOrigin_ReturnsPolicy()
{
// ✅ Arrange
var corsPolicyService = new CorsPolicyService();
var origin = "https://localhost:3000";
// ✅ Act
var result = await corsPolicyService.GetCorsPolicyAsync(origin);
// ✅ Assert
Assert.IsNotNull(result);
Assert.IsTrue(result.Origins.Contains(origin));
Assert.IsTrue(result.SupportsCredentials);
}
[TestMethod]
public void CorsService_CreateUser_WithValidData_CreatesSuccessfully()
{
// ✅ Arrange
var corsService = new CorsService();
var user = new Models.User
{
Name = "Test User",
Email = "test@example.com"
};
// ✅ Act
var result = corsService.CreateUserAsync(user).Result;
// ✅ Assert
Assert.IsNotNull(result);
Assert.AreEqual("Test User", result.Name);
Assert.AreEqual("test@example.com", result.Email);
}
[TestMethod]
public void CorsService_CreateUser_WithInvalidData_ThrowsException()
{
// ✅ Arrange
var corsService = new CorsService();
var invalidUser = new Models.User { Name = "", Email = "" };
// ✅ Act & Assert
// Note: This test would need to be updated to properly test validation
// The current implementation doesn't validate in the service layer
}
[TestCleanup]
public void Cleanup()
{
_httpClient?.Dispose();
_factory?.Dispose();
}
}
}
Best Practices for CORS Configuration
1. Never Use AllowAnyOrigin in Production
// ❌ Never do this in production
services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.AllowAnyOrigin() // ❌ Security risk
.AllowAnyMethod()
.AllowAnyHeader();
});
});
2. Specify Exact Origins
// ✅ Specify exact origins
services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.WithOrigins("https://myapp.com", "https://www.myapp.com")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
3. Be Specific with Headers and Methods
// ✅ Be specific about allowed headers and methods
services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.WithOrigins("https://myapp.com")
.WithHeaders("Content-Type", "Authorization", "X-Requested-With")
.WithMethods("GET", "POST", "PUT", "DELETE")
.AllowCredentials();
});
});
4. Use Environment-Specific Policies
// ✅ Different policies for different environments
if (env.IsDevelopment())
{
app.UseCors("DevelopmentPolicy");
}
else
{
app.UseCors("ProductionPolicy");
}
5. Validate Origin Dynamically
// ✅ Validate origins dynamically
public async Task<bool> IsOriginAllowedAsync(string origin)
{
// ✅ Check against allowed list or use regex patterns
var allowedOrigins = new[] { "https://myapp.com", "https://*.myapp.com" };
// Implementation to validate origin
}
6. Log CORS Requests
// ✅ Log CORS requests for monitoring
app.Use(async (context, next) =>
{
var origin = context.Request.Headers["Origin"].FirstOrDefault();
if (!string.IsNullOrEmpty(origin))
{
// ✅ Log the origin for monitoring
logger.LogInformation("CORS request from origin: {Origin}", origin);
}
await next();
});
Debugging Steps
Step 1: Check Browser Console
// ✅ Look for CORS error messages in browser console
// Example: "Access to fetch at '...' from origin '...' has been blocked by CORS policy"
Step 2: Verify CORS Headers
# ✅ Check response headers using curl
curl -H "Origin: https://example.com" -X OPTIONS -v https://your-api.com/api/data
Step 3: Test Preflight Request
# ✅ Test preflight request manually
curl -H "Origin: https://example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type" \
-X OPTIONS \
https://your-api.com/api/data
Step 4: Check Server Configuration
// ✅ Verify CORS is configured and enabled in Program.cs
app.UseCors("PolicyName"); // Make sure this is called before UseRouting()
Step 5: Test with Different Origins
// ✅ Test with various origins to isolate the issue
fetch('https://your-api.com/api/data', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include' // Test with and without credentials
});
Step 6: Enable Detailed Logging
// ✅ Enable CORS logging for debugging
services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.WithOrigins("https://myapp.com")
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowCredentials();
});
});
Common Mistakes to Avoid
1. Forgetting to Call UseCors
// ❌ Missing UseCors middleware
app.UseRouting();
app.UseAuthorization(); // ❌ CORS won't work without UseCors before UseRouting
2. Using AllowAnyOrigin with Credentials
// ❌ Security vulnerability
policy.AllowAnyOrigin()
.AllowCredentials(); // ❌ This combination is not allowed
3. Incorrect Middleware Order
// ❌ Wrong order
app.UseRouting();
app.UseCors(); // ❌ Should come before UseRouting
app.UseAuthorization();
4. Not Handling Preflight Requests
// ❌ Not allowing OPTIONS method
policy.WithMethods("GET", "POST"); // ❌ Preflight requests need OPTIONS
5. Hardcoding Origins in Production
// ❌ Hardcoded origins
policy.WithOrigins("https://localhost:3000"); // ❌ Use configuration instead
Performance Considerations
1. Cache CORS Results
// ✅ Set preflight cache duration
policy.PreflightMaxAge = TimeSpan.FromHours(1); // Cache preflight results
2. Minimize Allowed Origins
// ✅ Only allow necessary origins
policy.WithOrigins("https://myapp.com"); // ✅ Specific origin
3. Optimize Header Validation
// ✅ Only allow necessary headers
policy.WithHeaders("Content-Type", "Authorization"); // ✅ Specific headers
Security Considerations
1. Never Allow All Origins in Production
// ❌ Security risk
policy.AllowAnyOrigin(); // ❌ Never in production
2. Validate Origins Carefully
// ✅ Proper origin validation
policy.SetIsOriginAllowed(origin =>
origin.StartsWith("https://myapp.com")); // ✅ Validate origin properly
3. Be Cautious with Credentials
// ✅ Only allow credentials with specific origins
policy.WithOrigins("https://trusted.com")
.AllowCredentials(); // ✅ Safer than AllowAnyOrigin
4. Monitor CORS Requests
// ✅ Log and monitor CORS requests
// Watch for unusual origin patterns
Testing CORS Scenarios
1. Test Different Origins
[TestMethod]
public async Task CorsPolicy_WithValidOrigin_AllowsRequest()
{
var corsPolicyService = new CorsPolicyService();
var result = await corsPolicyService.IsOriginAllowedAsync("https://localhost:3000");
Assert.IsTrue(result);
}
2. Test Invalid Origins
[TestMethod]
public async Task CorsPolicy_WithInvalidOrigin_RejectsRequest()
{
var corsPolicyService = new CorsPolicyService();
var result = await corsPolicyService.IsOriginAllowedAsync("https://malicious.com");
Assert.IsFalse(result);
}
3. Test Preflight Requests
[TestMethod]
public async Task CorsPolicy_HandlesPreflightRequest()
{
var corsPolicyService = new CorsPolicyService();
var policy = await corsPolicyService.GetCorsPolicyAsync("https://localhost:3000");
Assert.IsTrue(policy.Methods.Contains("OPTIONS"));
Assert.IsTrue(policy.SupportsCredentials);
}
4. Test Credential Handling
[TestMethod]
public async Task CorsPolicy_WithCredentials_ConfiguredCorrectly()
{
var corsPolicyService = new CorsPolicyService();
var policy = await corsPolicyService.GetCorsPolicyAsync("https://localhost:3000");
Assert.IsTrue(policy.SupportsCredentials);
Assert.IsTrue(policy.AllowCredentials);
}
Alternative Solutions
1. Use Proxy Server
// ✅ Frontend proxy to avoid CORS issues
// Configure webpack dev server or nginx to proxy API requests
2. JSONP (Legacy Solution)
// ❌ JSONP is deprecated and insecure
// Only use if absolutely necessary for legacy systems
3. Server-Side API Gateway
// ✅ Use API gateway to handle cross-origin requests
// Centralized CORS management at the gateway level
Migration Checklist
- Configure CORS in Program.cs with appropriate policies
- Never use AllowAnyOrigin in production
- Specify exact allowed origins, headers, and methods
- Ensure UseCors is called before UseRouting
- Test preflight requests work correctly
- Validate origin patterns for security
- Set appropriate preflight cache duration
- Monitor CORS requests for security
- Document CORS configuration for team awareness
Conclusion
The ‘CORS Error’ is a common but preventable ASP.NET Core issue that occurs when cross-origin requests are not properly configured. By following the solutions provided in this guide—implementing proper CORS configuration, using environment-specific policies, applying fine-grained control, and following best practices—you can effectively prevent and resolve this error in your ASP.NET Core applications.
The key is to understand CORS security mechanisms, implement proper configuration patterns, use modern ASP.NET Core features correctly, and maintain clean, well-organized code. With proper CORS management, your ASP.NET Core applications will securely handle cross-origin requests while maintaining good security posture.
Remember to test your changes thoroughly, follow ASP.NET Core best practices for CORS configuration, implement proper security measures, and regularly review your CORS policies to ensure your applications maintain the best possible architecture and avoid common CORS errors.
Related Articles
Fix: 500 Internal Server Error in ASP.NET Core - Complete Error Handling Guide
Learn how to fix the 500 Internal Server Error in ASP.NET Core applications. This comprehensive guide covers error diagnosis, debugging techniques, and proper error handling implementation.
Fix: Cannot consume scoped service from singleton error in C#
Learn how to fix the 'Cannot consume scoped service from singleton' error in C# dependency injection. This comprehensive guide covers service lifetimes, proper registration patterns, and architectural solutions.
How to Fix: No service for type has been registered error in C# - Complete Dependency Injection Guide
Learn how to fix the 'No service for type has been registered' error in C# dependency injection. This comprehensive guide covers DI registration, service lifetimes, and proper configuration techniques.