Introduction
If you've worked with modern web applications, chances are you've encountered a frustrating browser error that looks something like this:
Access to fetch at 'https://api.example.com'
from origin 'https://app.example.com'
has been blocked by CORS policy.
For many developers, especially those new to frontend-backend integrations, CORS errors can seem mysterious. The API works perfectly in Postman, yet requests fail in the browser.
The reality is that CORS isn't a bug. It's a browser security feature designed to protect users from malicious websites.
In this guide, you'll learn what CORS is, why it exists, how ASP.NET Core handles CORS, common causes of failures, and how to configure CORS correctly in production environments.
What is CORS?
CORS (Cross-Origin Resource Sharing) is a browser security mechanism that controls how resources hosted on one origin can be accessed from another origin.
An origin consists of:
- Protocol
- Domain
- Port
For example:
| URL | Origin |
|---|---|
| https://example.com | Origin A |
| http://example.com | Different Origin |
| https://example.com:5001 | Different Origin |
| https://api.example.com | Different Origin |
Even though these URLs may belong to the same organization, browsers treat them as different origins.
Why It Matters
Modern applications often separate frontend and backend systems.
Example architecture:
Frontend (React)
https://app.company.com
Backend (ASP.NET Core API)
https://api.company.com
Without proper CORS configuration, browsers block communication between these applications.
Benefits of CORS:
- Prevents unauthorized cross-site requests
- Protects user data
- Reduces attack surface
- Enforces browser security policies
Key Concepts
Same-Origin Policy
Browsers allow scripts to access resources only from the same origin by default.
Example:
Allowed:
https://app.company.com
→ https://app.company.com/api
Blocked:
https://app.company.com
→ https://api.company.com
Unless CORS is configured.
Preflight Requests
Before sending certain requests, browsers send an OPTIONS request.
Example:
OPTIONS /api/products
The browser checks whether the server allows:
- HTTP method
- Headers
- Origin
Only after approval does the actual request execute.
Access-Control-Allow-Origin
Most important CORS header:
Access-Control-Allow-Origin:
https://app.company.com
This tells browsers which origins are permitted.
Step-by-Step Guide
Step 1: Install ASP.NET Core CORS Support
ASP.NET Core includes built-in CORS middleware.
No additional packages are required in modern versions.
Step 2: Register CORS Services
In Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowFrontend",
policy =>
{
policy.WithOrigins(
"https://app.company.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddControllers();
var app = builder.Build();
This creates a named CORS policy.
Step 3: Apply Middleware
app.UseRouting();
app.UseCors("AllowFrontend");
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Middleware order matters.
CORS should be configured before authorization and endpoint mapping.
Step 4: Test Requests
Frontend example:
fetch("https://api.company.com/api/products")
.then(response => response.json())
.then(data => console.log(data));
The browser will now allow communication.
Real-World Example
Imagine an enterprise application:
Frontend
React Application
https://portal.company.com
Backend
ASP.NET Core API
https://api.company.com
Without CORS:
Browser
↓
Blocked Request
↓
CORS Error
With CORS:
Browser
↓
Allowed Origin Check
↓
API Response
↓
Success
Architecture Diagram
graph LR
A[React Frontend]
--> B[CORS Middleware]
B --> C[ASP.NET Core API]
C --> D[SQL Server Database]
Allow Multiple Origins
Production systems often require multiple frontend applications.
builder.Services.AddCors(options =>
{
options.AddPolicy("ProductionPolicy",
policy =>
{
policy.WithOrigins(
"https://app.company.com",
"https://admin.company.com")
.AllowAnyMethod()
.AllowAnyHeader();
});
});
Allow Credentials
When cookies or authentication tokens are involved:
builder.Services.AddCors(options =>
{
options.AddPolicy("CredentialPolicy",
policy =>
{
policy.WithOrigins(
"https://app.company.com")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
Important:
.AllowCredentials()
cannot be combined with:
.AllowAnyOrigin()
ASP.NET Core will throw an exception.
Best Practices
Allow Specific Origins
Good:
.WithOrigins("https://app.company.com")
Bad:
.AllowAnyOrigin()
Separate Policies by Environment
Development:
https://localhost:3000
Production:
https://app.company.com
Store origins in configuration files.
Restrict Methods
Instead of:
.AllowAnyMethod()
Use:
.WithMethods(
"GET",
"POST")
when possible.
Restrict Headers
Avoid exposing unnecessary headers.
.WithHeaders(
"Content-Type",
"Authorization")
Common Mistakes to Avoid
Mistake 1: Middleware Order
Incorrect:
app.MapControllers();
app.UseCors();
Correct:
app.UseCors();
app.MapControllers();
Mistake 2: Using AllowAnyOrigin in Production
This exposes APIs unnecessarily.
Always specify allowed origins.
Mistake 3: Testing Only in Postman
Postman ignores browser CORS restrictions.
Always test from a browser.
Mistake 4: Ignoring Preflight Requests
Browsers often send:
OPTIONS
requests before actual API calls.
Ensure these requests aren't blocked.
Mistake 5: Missing HTTPS
Mixed content can cause behavior that resembles CORS failures.
Example:
Frontend:
https://app.company.com
API:
http://api.company.com
Use HTTPS everywhere.
Performance Considerations
Reduce Unnecessary Preflight Requests
Simple requests avoid preflight calls.
Prefer:
GET
POST
with standard headers when possible.
Cache Preflight Responses
Example:
policy.SetPreflightMaxAge(
TimeSpan.FromHours(1));
Benefits:
- Fewer OPTIONS requests
- Lower latency
- Better scalability
Minimize Allowed Origins
Smaller policy evaluation improves maintainability and security.
Security Considerations
CORS is not authentication.
Many developers incorrectly assume:
Configured CORS
=
Secured API
This is false.
You still need:
- Authentication
- Authorization
- Rate limiting
- API validation
- Input sanitization
Recommended security stack:
HTTPS
+
JWT Authentication
+
Authorization Policies
+
CORS
+
Rate Limiting
Pros and Cons
| Pros | Cons |
| Improves security | Can be confusing |
| Browser-supported | Easy to misconfigure |
| Fine-grained control | Requires testing |
| Supports modern architectures | Debugging may take time |
Frequently Asked Questions
1. What causes CORS errors?
CORS errors occur when a browser blocks requests between different origins because the server hasn't explicitly allowed the requesting origin.
2. Why does Postman work but the browser fails?
Postman doesn't enforce browser security policies. Browsers enforce CORS restrictions.
3. Can I disable CORS?
You can disable it in development environments, but it is not recommended for production systems.
4. Is AllowAnyOrigin safe?
Generally no. It should be avoided in production APIs.
5. Does CORS improve API security?
It improves browser security but does not replace authentication or authorization.
6. What is a preflight request?
A browser sends an OPTIONS request before certain cross-origin requests to verify permissions.
7. Does CORS affect mobile applications?
Typically no. Native mobile apps are not subject to browser-enforced CORS policies.
8. Can CORS be configured per controller?
Yes.
Example:
[EnableCors("AllowFrontend")]
public class ProductsController : ControllerBase
{
}
9. Why am I getting a CORS error after deployment?
Common reasons include incorrect origin URLs, HTTPS mismatches, missing middleware, or reverse proxy configuration issues.
10. How do I debug CORS issues?
Use:
- Browser Developer Tools
- Network Tab
- Response Headers
- Server Logs
Check for:
Access-Control-Allow-Origin
headers in responses.
Conclusion
CORS errors are among the most common issues encountered when building modern web applications with ASP.NET Core. Fortunately, once you understand the underlying concepts, resolving them becomes straightforward.
The key is to remember that CORS is a browser security feature—not an ASP.NET Core bug. Configure explicit origins, apply middleware correctly, handle preflight requests properly, and avoid overly permissive settings in production.
By following the practices covered in this guide, you'll create secure, scalable, and maintainable ASP.NET Core APIs that work seamlessly with modern frontend applications.


Comments (0)