Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin, access to selected resources from a different origin. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own.
An example of a cross-origin request: the front-end JavaScript code served from https://domain-a.com
uses to make a request for https://domain-b.com/data.json
.
For security reasons, browsers restrict cross-origin HTTP requests initiated from scripts. For example, XMLHttpRequest
and the Fetch API follow the same-origin policy. This means that a web application using those APIs can only request resources from the same origin the application was loaded from unless the response from other origins includes the right CORS headers.
The CORS mechanism supports secure cross-origin requests and data transfers between browsers and servers. Modern browsers use CORS in APIs such as XMLHttpRequest
or Fetch to mitigate the risks of cross-origin HTTP requests.
Who should read this article?
Everyone, really.
More specifically, this article is for web administrators, server developers, and front-end developers. Modern browsers handle the client side of cross-origin sharing, including headers and policy enforcement. But the CORS standard means servers have to handle new request and response headers. Another article for server developers discussing cross-origin sharing from a server perspective (with PHP code snippets) is supplementary reading.
What requests use CORS?
This cross-origin sharing standard can enable cross-site HTTP requests for:
- Invocations of the or Fetch APIs, as discussed above.
- Web Fonts (for cross-domain font usage in
@font-face
within CSS), so that servers can deploy TrueType fonts that can only be cross-site loaded and used by web sites that are permitted to do so. - WebGL textures.
- Images/video frames drawn to a canvas using .
- CSS Shapes from images.
This article is a general discussion of Cross-Origin Resource Sharing and includes a discussion of the necessary HTTP headers.
Functional overview
The Cross-Origin Resource Sharing standard works by adding new , or with certain request method, and then, upon “approval” from the server, sending the actual request. Servers can also inform clients whether “credentials” (such as Cookies and HTTP Authentication) should be sent with requests.
CORS failures result in errors, but for security reasons, specifics about the error are not available to JavaScript. All the code knows is that an error occurred. The only way to determine what specifically went wrong is to look at the browser’s console for details.
Subsequent sections discuss scenarios, as well as provide a breakdown of the HTTP headers used.
Examples of access control scenarios
We present three scenarios that demonstrate how Cross-Origin Resource Sharing works. All these examples use , which can make cross-site requests in any supporting browser.
A discussion of Cross-Origin Resource Sharing from a server perspective (including PHP code snippets) can be found in the Server-Side Access Control (CORS) article.
Simple requests
Some requests don’t trigger a CORS preflight. Those are called “simple requests” in this article, though the Fetch spec (which defines CORS) doesn’t use that term. A “simple request” is one that meets all the following conditions:
- One of the allowed methods:
- Apart from the headers automatically set by the user agent (for example, , , or the other headers defined in the Fetch spec as a “forbidden header name”), the only headers which are allowed to be manually set are those which the Fetch spec defines as a “CORS-safelisted request-header”, which are:
- (but note the additional requirements below)
DPR
Save-Data
Viewport-Width
Width
- The only allowed values for the header are:
application/x-www-form-urlencoded
multipart/form-data
text/plain
- No event listeners are registered on any object used in the request; these are accessed using the property.
- No object is used in the request.
Note: WebKit Nightly and Safari Technology Preview place additional restrictions on the values allowed in the , , and headers. If any of those headers have ”nonstandard” values, WebKit/Safari does not consider the request to be a “simple request”. What values WebKit/Safari consider “nonstandard” is not documented, except in the following WebKit bugs:
- Require preflight for non-standard CORS-safelisted request headers Accept, Accept-Language, and Content-Language
- Allow commas in Accept, Accept-Language, and Content-Language request headers for simple CORS
- Switch to a blacklist model for restricted Accept headers in simple CORS requests
No other browsers implement these extra restrictions, because they’re not part of the spec.
For example, suppose web content at https://foo.example
wishes to invoke content on domain https://bar.other
. Code of this sort might be used in JavaScript deployed on foo.example
:
This performs a simple exchange between the client and the server, using CORS headers to handle the privileges:
Let’s look at what the browser will send to the server in this case, and let’s see how the server responds:
GET /resources/public-data/ HTTP/1.1 Host: bar.other User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Connection: keep-alive Origin: https://foo.example
The request header of note is , which shows that the invocation is coming from https://foo.example
.
HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 00:23:53 GMT Server: Apache/2 Access-Control-Allow-Origin: * Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: application/xml […XML Data…]
In response, the server sends back an header. The use of the header and of show the access control protocol in its simplest use. In this case, the server responds with Access-Control-Allow-Origin: *
, which means that the resource can be accessed by any domain. If the resource owners at https://bar.other
wished to restrict access to the resource to requests only from https://foo.example
, they would send:
Access-Control-Allow-Origin: https://foo.example
Now no domain other than https://foo.example
can access the resource in a cross-site manner. To allow access to the resource, the Access-Control-Allow-Origin
header should contain the value that was sent in the request’s Origin
header.
Preflighted requests
Unlike method to the resource on the other domain, to determine if the actual request is safe to send. Cross-site requests are preflighted like this since they may have implications to user data.
The following is an example of a request that will be preflighted:
The example above creates an XML body to send with the POST
request. Also, a non-standard HTTP X-PINGOTHER
request header is set. Such headers are not part of HTTP/1.1, but are generally useful to web applications. Since the request uses a Content-Type
of application/xml
, and since a custom header is set, this request is preflighted.
Note: As described below, the actual POST
request does not include the Access-Control-Request-*
headers; they are needed only for the OPTIONS
request.
Let’s look at the full exchange between client and server. The first exchange is the preflight request/response:
Once the preflight request is complete, the real request is sent:
Lines 1 – 10 above represent the preflight request with the method. The browser determines that it needs to send this based on the request parameters that the JavaScript code snippet above was using, so that the server can respond whether it is acceptable to send the request with the actual request parameters. OPTIONS is an HTTP/1.1 method that is used to determine further information from servers, and is a safe method, meaning that it can’t be used to change the resource. Note that along with the OPTIONS request, two other request headers are sent (lines 9 and 10 respectively):
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
The header notifies the server as part of a preflight request that when the actual request is sent, it will be sent with a POST
request method. The header notifies the server that when the actual request is sent, it will be sent with a X-PINGOTHER
and Content-Type
custom headers. The server now has an opportunity to determine whether it wishes to accept a request under these circumstances.
Lines 13 – 22 above are the response that the server sends back indicating that the request method (POST
) and request headers (X-PINGOTHER
) are acceptable. In particular, let’s look at lines 16-19:
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
The server responds with Access-Control-Allow-Methods
and says that POST
and GET
are viable methods to query the resource in question. Note that this header is similar to the response header, but used strictly within the context of access control.
The server also sends Access-Control-Allow-Headers
with a value of “X-PINGOTHER, Content-Type
“, confirming that these are permitted headers to be used with the actual request. Like Access-Control-Allow-Methods
, Access-Control-Allow-Headers
is a comma separated list of acceptable headers.
Finally, gives the value in seconds for how long the response to the preflight request can be cached for without sending another preflight request. In this case, 86400 seconds is 24 hours. Note that each browser has a maximum internal value that takes precedence when the Access-Control-Max-Age
is greater.
Preflighted requests and redirects
Not all browsers currently support following redirects after a preflighted request. If a redirect occurs after a preflighted request, some browsers currently will report an error message such as the following.
The request was redirected to ‘https://example.com/foo’, which is disallowed for cross-origin requests that require preflight
Request requires preflight, which is disallowed to follow cross-origin redirect
The CORS protocol originally required that behavior but was subsequently changed to no longer require it. However, not all browsers have implemented the change, and so still exhibit the behavior that was originally required.
Until browsers catch up with the spec, you may be able to work around this limitation by doing one or both of the following:
- Change the server-side behavior to avoid the preflight and/or to avoid the redirect
- Change the request such that it is a simple request that doesn’t cause a preflight
If that’s not possible, then another way is to:
- Make a for the Fetch API, or ) to determine what URL the real preflighted request would end up at.
- Make another request (the “real” request) using the URL you obtained from
Response.url
orXMLHttpRequest.responseURL
in the first step.
However, if the request is one that triggers a preflight due to the presence of the Authorization
header in the request, you won’t be able to work around the limitation using the steps above. And you won’t be able to work around it at all unless you have control over the server the request is being made to.
Requests with credentials
The most interesting capability exposed by both or constructor when it is invoked.
In this example, content originally loaded from http://foo.example
makes a simple GET request to a resource on http://bar.other
which sets Cookies. Content on foo.example might contain JavaScript like this:
Line 7 shows the flag on that has to be set in order to make the invocation with Cookies, namely the withCredentials
boolean value. By default, the invocation is made without Cookies. Since this is a simple GET
request, it is not preflighted, but the browser will reject any response that does not have the : true
header, and not make the response available to the invoking web content.
Here is a sample exchange between client and server:
Although line 10 contains the Cookie destined for the content on http://bar.other
, if bar.other did not respond with an : true
(line 17) the response would be ignored and not made available to web content.
Credentialed requests and wildcards
When responding to a credentialed request, the server must specify an origin in the value of the Access-Control-Allow-Origin
header, instead of specifying the “*
” wildcard.
Because the request headers in the above example include a Cookie
header, the request would fail if the value of the Access-Control-Allow-Origin
header was “*”. But it does not fail: Because the value of the Access-Control-Allow-Origin
header is “http://foo.example
” (an actual origin) rather than the “*
” wildcard, the credential-cognizant content is returned to the invoking web content.
Note that the Set-Cookie
response header in the example above also sets a further cookie. In case of failure, an exception—depending on the API used—is raised.
Third-party cookies
Note that cookies set in CORS responses are subject to normal third-party cookie policies. In the example above, the page is loaded from foo.example
, but the cookie on line 20 is sent by bar.other
, and would thus not be saved if the user has configured their browser to reject all third-party cookies.
The HTTP response headers
This section lists the HTTP response headers that servers send back for access control requests as defined by the Cross-Origin Resource Sharing specification. The previous section gives an overview of these in action.
Access-Control-Allow-Origin
A returned resource may have one header, with the following syntax:
Access-Control-Allow-Origin: <origin> | *
Access-Control-Allow-Origin
specifies either a single origin, which tells browsers to allow that origin to access the resource; or else — for requests without credentials — the “*
” wildcard, to tell browsers to allow any origin to access the resource.
For example, to allow code from the origin https://mozilla.org
to access the resource, you can specify:
Access-Control-Allow-Origin: https://mozilla.org
Vary: Origin
If the server specifies a single origin (that may dynamically change based on the requesting origin as part of a white-list) rather than the “*
” wildcard, then the server should also include Origin
in the response header — to indicate to clients that server responses will differ based on the value of the request header.
Access-Control-Expose-Headers
The header lets a server whitelist headers that Javascript (such as ) in browsers are allowed to access.
Access-Control-Expose-Headers: <header-name>[, <header-name>]*
For example, the following:
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
…would allow the X-My-Custom-Header
and X-Another-Custom-Header
headers to be exposed to the browser.
Access-Control-Max-Age
The header indicates how long the results of a preflight request can be cached. For an example of a preflight request, see the above examples.
Access-Control-Max-Age: <delta-seconds>
The delta-seconds
parameter indicates the number of seconds the results can be cached.
Access-Control-Allow-Credentials
The header Indicates whether or not the response to the request can be exposed when the credentials
flag is true. When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials. Note that simple GET
requests are not preflighted, and so if a request is made for a resource with credentials, if this header is not returned with the resource, the response is ignored by the browser and not returned to web content.
Access-Control-Allow-Credentials: true
Credentialed requests are discussed above.
Access-Control-Allow-Methods
The header specifies the method or methods allowed when accessing the resource. This is used in response to a preflight request. The conditions under which a request is preflighted are discussed above.
Access-Control-Allow-Methods: <method>[, <method>]*
An example of a preflight request is given above, including an example which sends this header to the browser.
Access-Control-Allow-Headers
The header is used in response to a preflight request to indicate which HTTP headers can be used when making the actual request.
Access-Control-Allow-Headers: <header-name>[, <header-name>]*
The HTTP request headers
This section lists headers that clients may use when issuing HTTP requests in order to make use of the cross-origin sharing feature. Note that these headers are set for you when making invocations to servers. Developers using cross-site capability do not have to set any cross-origin sharing request headers programmatically.
Origin
The header indicates the origin of the cross-site access request or preflight request.
Origin: <origin>
The origin is a URI indicating the server from which the request initiated. It does not include any path information, but only the server name.
origin
value can be null
, or a URI.Note that in any access control request, the header is always sent.
Access-Control-Request-Method
The is used when issuing a preflight request to let the server know what HTTP method will be used when the actual request is made.
Access-Control-Request-Method: <method>
Examples of this usage can be found above.
Access-Control-Request-Headers
The header is used when issuing a preflight request to let the server know what HTTP headers will be used when the actual request is made.
Access-Control-Request-Headers: <field-name>[, <field-name>]*
Examples of this usage can be found above.
Specifications
Specification | Status | Comment |
---|---|---|
Living Standard | New definition; supplants W3C CORS specification. |
Browser compatibility
The compatibility table in this page is generated from structured data. If you’d like to contribute to the data, please check out https://github.com/mdn/browser-compat-data and send us a pull request.
Thank you!
Report sent
What happens next?
Our team will review your report. Once we verify the information you have supplied we will update this browser compatability table accordingly.
Can I keep track of my report?
You can join the GitHub repository to see updates and commits for this table data:
Tell us what’s wrong with this table
Our goal is to provide accurate, real values for all our compatibility data tables. Notifying MDN of inaccurate data or supplying new data pushes us further towards our goal of providing 100% real values to the developer community.
Thank you for helping.
Legend
- Full support
- Full support
Compatibility notes
Internet Explorer 8 and 9 expose CORS via the XDomainRequest
object, but have a full implementation in IE 10
See also
- CORS errors
- Enable CORS: I want to add CORS support to my server
- Fetch API
- Will it CORS? – an interactive CORS explainer & generator
- Using CORS with All (Modern) Browsers
- How to run Chrome browser without CORS
- Stack Overflow answer with “how to” info for dealing with common problems:
- How to avoid the CORS preflight
- How to use a CORS proxy to get around “No Access-Control-Allow-Origin header”
- How to fix “Access-Control-Allow-Origin header must not be the wildcard”