CORS: How to Handle It
What is CORS?
CORS (Cross-Origin Resource Sharing) is a method that uses extra HTTP headers to notify browsers to allow a web application operating on one origin to access resources from another origin.
Why does the browser need this mechanism?
The security policy known as the “Same-origin policy” is implemented by most browsers, and it controls how information loaded from one origin interacts with resources from another origin. CORS allows the same-origin policy to be relaxed. Browsers provide CORS to regulate communication between apps running at different origins, rather than completely preventing it.
When we talk about CORS, we’re talking about two separate programs attempting to communicate with one another, one of which is running in a browser.
How does it work?
When a client application running at origin A tries to access resources from a server application running at origin B, the browser will block communication between the two unless specific CORS HTTP headers are present in the request and response — otherwise a CORS error appears in the browser console.
Because the browser adds the CORS headers to the request, whether a client has access to a resource is largely determined by the server’s CORS response headers.
A preflight request is automatically sent by the browser for access requests that could potentially cause side effects on the server (such as a POST request that mutates data), and the server then approves or disapproves the actual request via CORS HTTP headers in response to that preflight.
CORS HTTP headers
They fall into two groups: request headers (set by the client) and response headers (set by the server).
Request headers (all set automatically by the browser):
Origin— specifies the origin of the client application.Access-Control-Request-Method— added during a preflight request to tell the server which HTTP method the actual request intends to send.Access-Control-Request-Headers— added during a preflight request to tell the server which HTTP headers the actual request intends to send.
Response headers:
Access-Control-Allow-Origin— specifies the origin the server wants to allow to access a resource. If this doesn’t match the client’sOriginheader, the client experiences CORS errors.Access-Control-Allow-Headers— the set of headers allowed in the client request.Access-Control-Allow-Methods— the set of methods allowed for the client request.Access-Control-Expose-Headers— the set of response headers the browser can expose to the client application.Access-Control-Max-Age— how long the result of a preflight request can be cached.Access-Control-Allow-Credentials— for a preflight request, whether the actual request can use credentials; for a non-preflighted CORS request using credentials (e.g. a browser cookie), whether the browser should allow the client app to access the response.
Handling CORS
Handling CORS is straightforward, and it’s virtually always something the server application needs to handle.
When replying to HTTP requests, your server can use the CORS response headers to tell
the client app whether it can access a resource — Access-Control-Allow-Origin for
which origin can request, Access-Control-Allow-Headers for which headers the client
can send, Access-Control-Allow-Methods for which HTTP methods it can use, and so on.
Here are a few server setups for enabling CORS.
CORS on Apache
Add the following inside the Directory, Location, Files, or VirtualHost sections
of your server config (usually httpd.conf or apache.conf), or within a .htaccess
file:
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
</IfModule>
Check your configuration for errors:
apachectl -t
Then reload Apache:
sudo service apache2 reload
or
apachectl -k graceful
Altering headers requires mod_headers, which is enabled by default in Apache — you can
confirm with:
a2enmod headers
Note: you can also use add instead of set, but add can add the header multiple
times, so set is usually safer.
CORS on ASP.NET
If you don’t have access to configure IIS, add the header directly in your source pages:
Response.AppendHeader("Access-Control-Allow-Origin", "*");
This works with IIS6, IIS7 Classic Mode, and IIS7 Integrated Mode.
ASP.NET Web API
ASP.NET Web API 2 supports CORS. Add the Microsoft.AspNet.WebApi.Cors NuGet package,
then:
public static void Register(HttpConfiguration config)
{
config.EnableCors();
}
To enable cross-origin requests on a specific controller or method, add the
EnableCors attribute:
[EnableCors(origins: "http://example.com", headers: "*", methods: "*")]
public class TestController : ApiController
{
// Controller methods not shown...
}
Or enable it globally without annotating each controller:
public static void Register(HttpConfiguration config)
{
var corsAttr = new EnableCorsAttribute("http://example.com", "*", "*");
config.EnableCors(corsAttr);
}
CORS on AWS API Gateway
API Gateway offers a CORS-enable button, but it only sets CORS correctly for a 200 response (not other status codes) and ignores some header support. The more reliable approach is to configure it manually:
- Log into the API Gateway console.
- Create all the REST resources and methods that need to be exposed before setting up CORS (repeat these steps if new resources/methods are added afterward).
- Select a resource.
- Add an OPTIONS method with integration type “mock”.
- For each method on the resource, go to Response Method.
- Add all the response codes that should be supported (200, 500, etc.).
- For each response code, set Response Headers to
X-Requested-With,Access-Control-Allow-Headers,Access-Control-Allow-Origin, andAccess-Control-Allow-Methods. - Go to Integration Response, select a response code, then Header Mappings, and insert default values, e.g.:
X-Requested-With: '*'
Access-Control-Allow-Headers: 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,x-requested-with'
Access-Control-Allow-Origin: '*'
Access-Control-Allow-Methods: 'POST,GET,OPTIONS'
- Repeat for each method, including the new OPTIONS method.
- Deploy the API to a stage.
- Verify CORS requests work using a CORS test client.
CORS on ExpressJS
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "YOUR-DOMAIN.TLD");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.get('/', function(req, res, next) {
// Handle the get for this route
});
app.post('/', function(req, res, next) {
// Handle the post for this route
});
CORS on IIS7
Merge this into the web.config file at the root of your application or site:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
CORS on Spring Boot applications in Kotlin
A WebFilter enables CORS on Spring Boot apps using the WebFlux starter:
import org.springframework.http.HttpMethod
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange
import org.springframework.web.server.WebFilter
import org.springframework.web.server.WebFilterChain
import reactor.core.publisher.Mono
@Component
class CorsFilter : WebFilter {
override fun filter(ctx: ServerWebExchange?, chain: WebFilterChain?): Mono<Void> {
if (ctx != null) {
ctx.response.headers.add("Access-Control-Allow-Origin", "*")
ctx.response.headers.add("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS")
ctx.response.headers.add("Access-Control-Allow-Headers", "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range")
if (ctx.request.method == HttpMethod.OPTIONS) {
ctx.response.headers.add("Access-Control-Max-Age", "1728000")
ctx.response.statusCode = HttpStatus.NO_CONTENT
return Mono.empty()
} else {
ctx.response.headers.add("Access-Control-Expose-Headers", "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range")
return chain?.filter(ctx) ?: Mono.empty()
}
} else {
return chain?.filter(ctx) ?: Mono.empty()
}
}
}
CORS on Nginx
# Wide-open CORS config for nginx
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
}
}
CORS on PHP
<?php
header("Access-Control-Allow-Origin: *");
Note: as with all uses of the PHP header function, this must run before any output is
sent from the server.
CORS on Tomcat
Apache Tomcat includes CORS support from version 7.0.41 onward. A minimal configuration:
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>