Configuring CORS in ASP.NET Core (2024)

Configuring CORS in ASP.NET Core (2)

If you are not familiar with the Same-Origin Policy (SOP) or the Cross-Origin Resource Sharing (CORS) policy, I highly recommend reading my previous post (https://emrebener.medium.com/a-comprehensive-guide-to-the-same-origin-policy-and-the-cors-policy-4ca7535b0145) where I explained SOP and CORS in detail. This post assumes a basic understanding of these policies and focuses on configuring CORS in an ASP.NET Core environment.

If you’re here to resolve an error you’re encountering, I encourage you to first understand the policies involved before focusing on addressing the error.

Configuring CORS in an ASP.NET Core project to enable cross-origin requests involves adding CORS services to the service container, and then either registering the CORS middleware in the pipeline, or using CORS attributes to enable CORS.

1.1. Service Registration & Policy Configurations

The first step to configuring CORS policy in an ASP.NET Core application is to add the CORS services to the project. This is achieved using the AddCors() method, which takes CORS settings as input and registers all necessary services (which, if you are curious, includes the CORS service itself, a default policy provider, and various options-related services — more on this later).

With the AddCors() call, you can register as many policies as you need. For example;

var builder = WebApplication.CreateBuilder(args);

// ...

builder.Services.AddCors(options =>
{
options.AddPolicy(name: "AllowAllOrigins",
configurePolicy: policy =>
{
policy.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
options.AddPolicy(name: "AllowOnlySomeOrigins",
configurePolicy: policy =>
{
policy.WithOrigins("https://example1.com",
"https://example2.com");
});
});

// ...

var app = builder.Build();

// ...

In this case, we registered two policies which are named “AllowAllOrigins” and “AllowOnlySomeOrigins”. The “AllowAllOrigins” policy is configured to esentially disable CORS policy, and the “AllowOnlySomeOrigins” is configured to only allow cross-origin requests from example1.com and example2.com.

The next step is to enable CORS using a policy, which will be explained in the upcoming section. Typically, when using the middleware approach, you will only need and use a single policy.

1.2. Middleware Registration

To enable CORS, you can either register the built-in CORS middleware with UseCors() which enables CORS across all endpoints, or you can use endpoint routing with RequireCors() to enable CORS only for specific endpoints. Both of these approaches are explained in the upcoming sections.

The CORS middleware uses the CORS services you added to the service container to enable CORS policy across all endpoints in the project. The CORS middleware is registered with the UseCors() method, which optionally takes policy name as parameter. For example;

// ...

var myPolicyName = "MyPolicyName"; // you will specify the exact same string in different places, so assigning policy names to variables avoids potential typo mistakes.
builder.Services.AddCors(options =>
{
options.AddPolicy(name: myPolicyName,
configurePolicy: policy =>
{
policy.WithOrigins("http://example1.com",
"http://example2.com");
});
});

// ...

var app = builder.Build();

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseCors(myPolicyName);

app.UseAuthorization();

// ...

1.2.1. Using Default Policy

By explicitly specifying the default policy with AddDefaultPolicy(), you can then use UseCors() or RequireCors() without needing to specify a policy name as a parameter. Additionally, with a default policy set, you can use the [EnableCors] attribute without having to specify a policy name as well. The idea is that, when a default policy is set, the CORS middleware and services will know which policy to use when a policy is not explicitly specified.

Note that even if you have only one policy registered, simply calling UseCors() without specifying a policy name will not enable CORS. Therefore, you need to call AddDefaultPolicy() for atleast one policy definition if you don’t wish to provide a policy name for UseCors(), RequireCors(), or [EnableCors].

Adding a default policy is exactly the same as adding any policy, except you call AddDefaultPolicy() instead of AddPolicy().

1.2.2. The Simplest Way⭐

I want to note that there are different approaches available to enabling CORS in an ASP.NET Core project. Truth is; if you will only have a single policy in your project, which is often the case, can skip adding CORS services to the service container.

This is because the services added by AddCors are primarily for decision-making (to determine which policy to use) and providing policy configurations, which are unnecessary when you only have a single policy defined in the project.

Therefore, if you only need a single policy to be used project-wide, my recommendation will be to avoid AddCors altogether and to specify the policy directly in the UseCors call, which would look like;

app.UseCors(configurePolicy: policy =>
{
policy.WithOrigins("https://example1.com",
"https://example2.com");
});

In this approach, you specify the policy itself as the parameter instead of its name, which eliminates the need for CORS services.

1.2.3. Regarding Middleware Order

As always with registering middlewares, you must be cautious about the placement of the CORS middleware in the pipeline. For example, you would not want to call UseResponseCaching() before the CORS middleware (if you are using response caching).

1.2.3.1. Important Note for JavaScript Clients

Typically, UseStaticFiles() is called BEFORE the UseCors(). However, when using JavaScript clients (e.g., Angular, React), you must call UseCors() before UseStaticFiles().

1.3. Enabling CORS With Attributes

As I’ve mentioned, using the middleware approach ensures the same CORS policy across all endpoints in the project. However, if you require fine-grained control over which endpoints have CORS enabled and with what policy, you can use the [EnableCors] attribute.

It’s important to note that the need for this level of control is rare, and you most likely won’t need to configure CORS on specific endpoints using attributes.

The [EnableCors] attribute can be applied to razor page PageModels, controllers or controller action methods. You can use [EnableCors] to specify the default policy, or [EnableCors("PolicyNameGoesHere")] to specify a particular policy.

Note that when the [EnableCors] attribute is applied to an endpoint AND the CORS middleware is registered in pipeline, both of the policies will be applied. However, this is not recommended. You should choose either of the two approaches and not combine them. In other words. CORS attributes are only relevant if you are not using the CORS middleware.

Here is how you can enable CORS with a specific policy on a controller action;

public class MyController : Controller
{

// ...

[EnableCors("PolicyName")] // enables CORS with the specified policy for this action
public IActionResult FunCatFact()
{
return Ok("A group of cats is called a clowder");
}

// ...

}

1.3.1. [DisableCors] Attribute

The [DisableCors] attribute can be used when using CORS middleware (UseCors()) to disable CORS policy for specific endpoints, or when you have [EnableCors] on controller-level and want to disable CORS on specific controller actions. For example;

[EnableCors] // if there is a default policy set, you don't have to specify policy name
public class MyController : Controller
{

// ...

[DisableCors] // this will disable CORS for this action
public IActionResult FunCatFact()
{
return Ok("A group of cats is called a clowder");
}

// this action will have CORS enabled, since it was enabled on the controller
public IActionResult FunDogFact()
{
return Ok("Dogs dream just like humans do");
}

// ...

}

This section provides an explanation of each configurable CORS setting, detailing their actual effects and how to configure them.

2.1. Allowing Origins

  • AllowAnyOrigin: Allows cross-origin requests from all origins, accepting any scheme (both http and https). Using this setting is highly discouraged.
    Effect; Access-Control-Allow-Origin: * header gets added to CORS responses.
    Usage; policy.AllowAnyOrigin()
  • WithOrigins: Specifies a list of origins that are allowed to initiate cross-domain requests.
    Effect; Access-Control-Allow-Origin header gets added to CORS responses.
    Usage; policy.WithOrigins("http://example1.com", "http://example2.com")

2.2. Allowing Methods

  • AllowAnyMethod: Allows any HTTP method with cross-origin requests.
    Effect; Access-Control-Allow-Methods: * header gets added to CORS responses.
    Usage; policy.AllowAnyMethod()
  • WithMethods: Specifies a list of methods to be allowed in cross-origin requests.
    Effect; Access-Control-Allow-Methods header gets added to CORS responses.
    Usage; policy.WithMethods("GET", "POST")

2.3. Allowing Headers

  • AllowAllHeaders: Simply allows all headers in cross-origin requests. Effect; Access-Control-Allow-Headers: * header gets added to CORS responses.
    Usage; policy.AllowAnyHeader()
  • WithHeaders: Specifies a list of headers that are accepted in cross-origin requests.
    Effect; Access-Control-Allow-Headers header gets added to the CORS responses.
    Usage; policy.WithHeaders(HeaderNames.AcceptCharset, "X-MyCustomHeader", "X-MyOtherCustomHeader")
  • WithExposedHeaders: Specifies a list of headers that can be exposed/shown to the calling script. By default, browsers will filter out all non-CORS-safelisted headers from the responses (for a detailed breakdown of CORS-safelisted request headers, you can refer to the CORS Specification).
  • Effect; Access-Control-Expose-Headers header gets added to CORS responses.
    Usage; policy.WithExposedHeaders("X-MyCustomHeader", "X-MyOtherCustomHeader")
Configuring CORS in ASP.NET Core (3)

2.4. Allowing/Disallowing Credentialed Requests

  • AllowCredentials: Specifies whether credentialed requests are allowed. Effect; Access-Control-Allow-Credentials: true header gets added to CORS responses.
    Usage; policy.AllowCredentials()
  • DisallowCredentials: Sets the policy to not accept credentialed requests. Effect; Access-Control-Allow-Credentials: false header gets added to CORS responses.
    Usage; policy.DisallowCredentials()

2.5. Setting Preflight Cache Duration

  • SetPreflightMaxAge: Specifies the duration for which the preflight response can be cached.
    Effect; Access-Control-Max-Age header gets added to CORS responses.
    Usage; policy.SetPreflightMaxAge(TimeSpan.FromSeconds(55)) (55 seconds as an example)

Note that if you called AllowCredentials, you can’t also call AllowAnyOrigin, AllowAnyMethod, or AllowAllHeaders on the same policy, because CORS spec prohibits any wildcards in credentialed requests.

Just as you can delegate tasks like HSTS and HTTPS redirection to IIS, you can also delegate the management of CORS policies to the web server.

If the server doesn’t allow anonymous access, CORS needs to run before Windows Authentication will have to run before CORS. The IIS CORS module makes this possible.

On the other hand, you will not be able to run your projects locally (if there are CORS errors). For this reason, even if you choose to offload CORS to IIS in production, you will still need to register CORS services and middleware conditionally in your code by checking the current development environment.

You can install IIS CORS module from https://www.iis.net/downloads/microsoft/iis-cors-module.

Configuring CORS in ASP.NET Core (2024)

References

Top Articles
Latest Posts
Article information

Author: Greg Kuvalis

Last Updated:

Views: 6185

Rating: 4.4 / 5 (55 voted)

Reviews: 94% of readers found this page helpful

Author information

Name: Greg Kuvalis

Birthday: 1996-12-20

Address: 53157 Trantow Inlet, Townemouth, FL 92564-0267

Phone: +68218650356656

Job: IT Representative

Hobby: Knitting, Amateur radio, Skiing, Running, Mountain biking, Slacklining, Electronics

Introduction: My name is Greg Kuvalis, I am a witty, spotless, beautiful, charming, delightful, thankful, beautiful person who loves writing and wants to share my knowledge and understanding with you.