aspnetcore/security/cors/includes/cors7.md
:::moniker range="= aspnetcore-7.0"
By Rick Anderson and Kirk Larkin
This article shows how to enable CORS in an ASP.NET Core app.
Browser security prevents a web page from making requests to a different domain than the one that served the web page. This restriction is called the same-origin policy. The same-origin policy prevents a malicious site from reading sensitive data from another site. Sometimes, you might want to allow other sites to make cross-origin requests to your app. For more information, see the Mozilla CORS article.
Cross Origin Resource Sharing (CORS):
View or download sample code (how to download)
Two URLs have the same origin if they have identical schemes, hosts, and ports (RFC 6454).
These two URLs have the same origin:
https://example.com/foo.htmlhttps://example.com/bar.htmlThese URLs have different origins than the previous two URLs:
https://example.net: Different domainhttps://www.example.com/foo.html: Different subdomainhttp://example.com/foo.html: Different schemehttps://example.com:9000/foo.html: Different portThere are three ways to enable CORS:
Using the [EnableCors] attribute with a named policy provides the finest control in limiting endpoints that support CORS.
[!WARNING] xref:Microsoft.AspNetCore.Builder.CorsMiddlewareExtensions.UseCors%2A must be called in the correct order. For more information, see Middleware order. For example,
UseCorsmust be called before xref:Microsoft.AspNetCore.Builder.ResponseCachingExtensions.UseResponseCaching%2A when usingUseResponseCaching.
Each approach is detailed in the following sections.
<a name="np"></a>
CORS Middleware handles cross-origin requests. The following code applies a CORS policy to all the app's endpoints with the specified origins:
The preceding code:
_myAllowSpecificOrigins. The policy name is arbitrary._myAllowSpecificOrigins CORS policy. UseCors adds the CORS middleware. The call to UseCors must be placed after UseRouting, but before UseAuthorization. For more information, see Middleware order.WithOrigins, are described later in this article._myAllowSpecificOrigins CORS policy for all controller endpoints. See endpoint routing to apply a CORS policy to specific endpoints.With endpoint routing, the CORS middleware must be configured to execute between the calls to UseRouting and UseEndpoints.
The xref:Microsoft.Extensions.DependencyInjection.MvcCorsMvcCoreBuilderExtensions.AddCors%2A method call adds CORS services to the app's service container:
For more information, see CORS policy options in this document.
The xref:Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder methods can be chained, as shown in the following code:
Note: The specified URL must not contain a trailing slash (/). If the URL terminates with /, the comparison returns false and no header is returned.
<a name="uc1"></a>
[!WARNING]
UseCorsmust be placed afterUseRoutingand beforeUseAuthorization. This is to ensure that CORS headers are included in the response for both authorized and unauthorized calls.
Typically, UseStaticFiles is called before UseCors. Apps that use JavaScript to retrieve static files cross site must call UseCors before UseStaticFiles.
<a name="dp"></a>
The following highlighted code enables the default CORS policy:
The preceding code applies the default CORS policy to all controller endpoints.
<a name="ecors6"></a>
With endpoint routing, CORS can be enabled on a per-endpoint basis using the xref:Microsoft.AspNetCore.Builder.CorsEndpointConventionBuilderExtensions.RequireCors%2A set of extension methods:
In the preceding code:
app.UseCors enables the CORS middleware. Because a default policy hasn't been configured, app.UseCors() alone doesn't enable CORS./echo and controller endpoints allow cross-origin requests using the specified policy./echo2 and Razor Pages endpoints do not allow cross-origin requests because no default policy was specified.The [DisableCors] attribute does not disable CORS that has been enabled by endpoint routing with RequireCors.
In .NET 7, the [EnableCors] attribute must pass a parameter or an ASP0023 Warning is generated from a ambiguous match on the route. .NET 8 or later doesn't generate the ASP0023 warning.
See Test CORS with [EnableCors] attribute and RequireCors method for instructions on testing code similar to the preceding.
<a name="attr"></a>
Enabling CORS with the [EnableCors] attribute and applying a named policy to only those endpoints that require CORS provides the finest control.
The [EnableCors] attribute provides an alternative to applying CORS globally. The [EnableCors] attribute enables CORS for selected endpoints, rather than all endpoints:
[EnableCors] specifies the default policy.[EnableCors("{Policy String}")] specifies a named policy.The [EnableCors] attribute can be applied to:
PageModelDifferent policies can be applied to controllers, page models, or action methods with the [EnableCors] attribute. When the [EnableCors] attribute is applied to a controller, page model, or action method, and CORS is enabled in middleware, both policies are applied. We recommend against combining policies. Use the [EnableCors] attribute or middleware, not both in the same app.
The following code applies a different policy to each method:
The following code creates two CORS policies:
For the finest control of limiting CORS requests:
[EnableCors("MyPolicy")] with a named policy.The code in the next section meets the preceding list.
<a name="dc6"></a>
The [DisableCors] attribute does not disable CORS that has been enabled by endpoint routing.
The following code defines the CORS policy "MyPolicy":
The following code disables CORS for the GetValues2 action:
The preceding code:
"MyPolicy" CORS policy for the controller.GetValues2 method.See Test CORS for instructions on testing the preceding code.
<a name="cpo6"></a>
This section describes the various options that can be set in a CORS policy:
xref:Microsoft.AspNetCore.Cors.Infrastructure.CorsOptions.AddPolicy%2A is called in Program.cs. For some options, it may be helpful to read the How CORS works section first.
xref:Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder.AllowAnyOrigin%2A: Allows CORS requests from all origins with any scheme (http or https). AllowAnyOrigin is insecure because any website can make cross-origin requests to the app.
[!NOTE] Specifying
AllowAnyOriginandAllowCredentialsis an insecure configuration and can result in cross-site request forgery. The CORS service returns an invalid CORS response when an app is configured with both methods.
AllowAnyOrigin affects preflight requests and the Access-Control-Allow-Origin header. For more information, see the Preflight requests section.
xref:Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder.SetIsOriginAllowedToAllowWildcardSubdomains%2A: Sets the xref:Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicy.IsOriginAllowed%2A property of the policy to be a function that allows origins to match a configured wildcard domain when evaluating if the origin is allowed.
In the preceding code, SetIsOriginAllowedToAllowWildcardSubdomains is called with the wildcard origin "https://*.example.com". This configuration allows CORS requests from any subdomain of example.com, such as https://subdomain.example.com or https://api.example.com. The * wildcard character must be included in the origin to enable wildcard subdomain matching.
xref:Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder.AllowAnyMethod%2A:
Access-Control-Allow-Methods header. For more information, see the Preflight requests section.To allow specific headers to be sent in a CORS request, called author request headers, call xref:Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder.WithHeaders%2A and specify the allowed headers:
To allow all author request headers, call xref:Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder.AllowAnyHeader%2A:
AllowAnyHeader affects preflight requests and the Access-Control-Request-Headers header. For more information, see the Preflight requests section.
A CORS Middleware policy match to specific headers specified by WithHeaders is only possible when the headers sent in Access-Control-Request-Headers exactly match the headers stated in WithHeaders.
For instance, consider an app configured as follows:
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
CORS Middleware declines a preflight request with the following request header because Content-Language (HeaderNames.ContentLanguage) isn't listed in WithHeaders:
Access-Control-Request-Headers: Cache-Control, Content-Language
The app returns a 200 OK response but doesn't send the CORS headers back. Therefore, the browser doesn't attempt the cross-origin request.
By default, the browser doesn't expose all of the response headers to the app. For more information, see W3C Cross-Origin Resource Sharing (Terminology): Simple Response Header.
The response headers that are available by default are:
Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragmaThe CORS specification calls these headers simple response headers. To make other headers available to the app, call xref:Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder.WithExposedHeaders%2A:
Credentials require special handling in a CORS request. By default, the browser doesn't send credentials with a cross-origin request. Credentials include cookies and HTTP authentication schemes. To send credentials with a cross-origin request, the client must set XMLHttpRequest.withCredentials to true.
Using XMLHttpRequest directly:
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;
Using jQuery:
$.ajax({
type: 'get',
url: 'https://www.example.com/api/test',
xhrFields: {
withCredentials: true
}
});
Using the Fetch API:
fetch('https://www.example.com/api/test', {
credentials: 'include'
});
The server must allow the credentials. To allow cross-origin credentials, call xref:Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder.AllowCredentials%2A:
The HTTP response includes an Access-Control-Allow-Credentials header, which tells the browser that the server allows credentials for a cross-origin request.
If the browser sends credentials but the response doesn't include a valid Access-Control-Allow-Credentials header, the browser doesn't expose the response to the app, and the cross-origin request fails.
Allowing cross-origin credentials is a security risk. A website at another domain can send a signed-in user's credentials to the app on the user's behalf without the user's knowledge. <!-- TODO Review: When using `AllowCredentials`, all CORS enabled domains must be trusted. I don't like "all CORS enabled domains must be trusted", because it implies that if you're not using `AllowCredentials`, domains don't need to be trusted. -->
The CORS specification also states that setting origins to "*" (all origins) is invalid if the Access-Control-Allow-Credentials header is present.
<a name="pref"></a>
For some CORS requests, the browser sends an additional OPTIONS request before making the actual request. This request is called a preflight request. The browser can skip the preflight request if all the following conditions are true:
Accept, Accept-Language, Content-Language, Content-Type, or Last-Event-ID.Content-Type header, if set, has one of the following values:
application/x-www-form-urlencodedmultipart/form-datatext/plainThe rule on request headers set for the client request applies to headers that the app sets by calling setRequestHeader on the XMLHttpRequest object. The CORS specification calls these headers author request headers. The rule doesn't apply to headers the browser can set, such as User-Agent, Host, or Content-Length.
The following is an example response similar to the preflight request made from the [Put test] button in the Test CORS section of this document.
General:
Request URL: https://cors3.azurewebsites.net/api/values/5
Request Method: OPTIONS
Status Code: 204 No Content
Response Headers:
Access-Control-Allow-Methods: PUT,DELETE,GET
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f8...8;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Vary: Origin
Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
The preflight request uses the HTTP OPTIONS method. It may include the following headers:
User-Agent.If the preflight request is denied, the app returns a 200 OK response but doesn't set the CORS headers. Therefore, the browser doesn't attempt the cross-origin request. For an example of a denied preflight request, see the Test CORS section of this document.
Using the F12 tools, the console app shows an error similar to one of the following, depending on the browser:
https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5. (Reason: CORS request did not succeed). Learn MoreTo allow specific headers, call xref:Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder.WithHeaders%2A:
To allow all author request headers, call xref:Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder.AllowAnyHeader%2A:
Browsers aren't consistent in how they set Access-Control-Request-Headers. If either:
"*"Accept, Content-Type, and Origin, plus any custom headers that you want to support.<a name="apf"></a>
When the CORS policy is applied either:
app.UseCors in Program.cs.[EnableCors] attribute.ASP.NET Core responds to the preflight OPTIONS request.
The Test CORS section of this document demonstrates this behavior.
<a name="pro6"></a>
When CORS is enabled with the appropriate policy, ASP.NET Core generally responds to CORS preflight requests automatically.
The following code uses the [HttpOptions] attribute to create endpoints for OPTIONS requests:
See Test CORS with [EnableCors] attribute and RequireCors method for instructions on testing the preceding code.
The Access-Control-Max-Age header specifies how long the response to the preflight request can be cached. To set this header, call xref:Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder.SetPreflightMaxAge%2A:
<a name="how-cors"></a>
This section describes what happens in a CORS request at the level of the HTTP messages.
<script> tag to receive the response. Scripts are allowed to be loaded cross-origin.The CORS specification introduced several new HTTP headers that enable cross-origin requests. If a browser supports CORS, it sets these headers automatically for cross-origin requests. Custom JavaScript code isn't required to enable CORS.
Select the PUT test button on the deployed sample.
The Origin header:
General headers
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
Response headers
Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET
Request headers
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...
In OPTIONS requests, the server sets the Response headers Access-Control-Allow-Origin: {allowed origin} header in the response. For example, in the sample code, the Delete [EnableCors] button OPTIONS request contains the following headers:
General headers
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
Response headers
Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET
Request headers
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
In the preceding Response headers, the server sets the Access-Control-Allow-Origin header in the response. The https://cors1.azurewebsites.net value of this header matches the Origin header from the request.
If xref:Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder.AllowAnyOrigin%2A is called, the Access-Control-Allow-Origin: *, the wildcard value, is returned. AllowAnyOrigin allows any origin.
If the response doesn't include the Access-Control-Allow-Origin header, the cross-origin request fails. Specifically, the browser disallows the request. Even if the server returns a successful response, the browser doesn't make the response available to the client app.
<a name="no-http"></a>
Requests to an endpoint using HTTP that are redirected to HTTPS by xref:Microsoft.AspNetCore.Builder.HttpsPolicyBuilderExtensions.UseHttpsRedirection%2A fail with ERR_INVALID_REDIRECT on the CORS preflight request.
API projects can reject HTTP requests rather than use UseHttpsRedirection to redirect requests to HTTPS.
<a name="options"></a>
When deploying to IIS, CORS has to run before Windows Authentication if the server isn't configured to allow anonymous access. To support this scenario, the IIS CORS module needs to be installed and configured for the app.
<a name="testc6"></a>
The sample download has code to test CORS. See how to download. The sample is an API project with Razor Pages added:
[!WARNING]
WithOrigins("https://localhost:<port>");should only be used for testing a sample app similar to the download sample code.
The following ValuesController provides the endpoints for testing:
MyDisplayRouteInfo is provided by the Rick.Docs.Samples.RouteInfo NuGet package and displays route information.
Test the preceding sample code by using one of the following approaches:
dotnet run using the default URL of https://localhost:5001.https://localhost:44398.Using a browser with the F12 tools:
Select the Values button and review the headers in the Network tab.
Select the PUT test button. See Display OPTIONS requests for instructions on displaying the OPTIONS request. The PUT test creates two requests, an OPTIONS preflight request and the PUT request.
Select the GetValues2 [DisableCors] button to trigger a failed CORS request. As mentioned in the document, the response returns 200 success, but the CORS request is not made. Select the Console tab to see the CORS error. Depending on the browser, an error similar to the following is displayed:
Access to fetch at 'https://cors1.azurewebsites.net/api/values/GetValues2' from origin 'https://cors3.azurewebsites.net' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
CORS-enabled endpoints can be tested with a tool, such as curl or Fiddler. When using a tool, the origin of the request specified by the Origin header must differ from the host receiving the request. If the request isn't cross-origin based on the value of the Origin header:
The following command uses curl to issue an OPTIONS request with information:
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i
<a name="tcer"></a>
Consider the following code which uses endpoint routing to enable CORS on a per-endpoint basis using RequireCors:
Notice that only the /echo endpoint is using the RequireCors to allow cross-origin requests using the specified policy. The controllers below enable CORS using [EnableCors] attribute.
The following TodoItems1Controller provides endpoints for testing:
The Delete [EnableCors] and GET [EnableCors] buttons succeed, because the endpoints have [EnableCors] and respond to preflight requests. The other endpoints fails. The GET button fails, because the JavaScript sends:
headers: {
"Content-Type": "x-custom-header"
},
The following TodoItems2Controller provides similar endpoints, but includes explicit code to respond to OPTIONS requests:
The preceding code can be tested by deploying the sample to Azure.In the Controller drop down list, select Preflight and then Set Controller. All the CORS calls to the TodoItems2Controller endpoints succeed.
:::moniker-end