Share cross-origin resources safely
The browser's same-origin policy blocks reading a resource from a different origin. This mechanism stops malicious sites from reading other sites' data, but it also prevents legitimate uses.
Modern web apps often want to get resources from a different origin, for
example, retrieving JSON data from a different domain or load images from
another site into a <canvas>
element. These can be public resources that
should be available for anyone to read, but the same-origin policy blocks their
use. Developers have historically used workarounds such as
JSONP.
Cross-Origin Resource Sharing (CORS) fixes this issue in a standardized way. Enabling CORS lets the server tell the browser it can use an additional origin.
How does a resource request work on the web?
A browser and a server can exchange data over the network using the Hypertext Transfer Protocol (HTTP). HTTP defines the communication rules between the requester and the responder, including what information is needed to get a resource.
The HTTP header negotiates the message exchange between the client and the server, and is used to determine access. Both the browser's request and the server's response message are divided into a header and a body.
Header
Information about the message such as the type of message or the encoding of the message. A header can include a variety of information expressed as key-value pairs. The request header and response header contain different information.
Sample request header
Accept: text/html
Cookie: Version=1
This header is equivalent to saying "I want to receive HTML in response. Here is a cookie I have."
Sample response header
Content-Encoding: gzip
Cache-Control: no-store
This header is equivalent to saying "The data in this response is encoded with gzip. Don't cache this."
Body
The message itself. This can be plain text, an image binary, JSON, HTML, or many other formats.
How does CORS work?
The same-origin policy tells the browser to block cross-origin requests. When you need a public resource from a different origin, the resource-providing server tells the browser that the origin sending the request can access its resource. The browser remembers that and allows cross-origin resource sharing for that resource.
Step 1: client (browser) request
When the browser makes a cross-origin request, the browser adds an Origin
header with the current origin (scheme, host, and port).
Step 2: server response
When a server sees this header, and wants to allow access, it adds an
Access-Control-Allow-Origin
header to the response specifying the requesting
origin (or *
to allow any origin.)
Step 3: browser receives response
When the browser sees this response with an appropriate
Access-Control-Allow-Origin
header, it shares the response data with the
client site.
Share credentials with CORS
For privacy reasons, CORS is normally used for anonymous requests, in which the requester isn't identified. If you want to send cookies when using CORS, which can identify the sender, you need to add additional headers to the request and response.
Request
Add credentials: 'include'
to the fetch options as in the following example.
This includes the cookie with the request as follows:
fetch('https://example.com', {
mode: 'cors',
credentials: 'include'
})
Response
Access-Control-Allow-Origin
must be set to a specific origin (no wildcard
using *
) and Access-Control-Allow-Credentials
must be set to true
.
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true
Preflight requests for complex HTTP calls
When a web app makes a complex HTTP request, the browser adds a preflight request to the beginning of the request chain.
The CORS specification defines a complex request as follows:
- A request that uses methods other than GET, POST, or HEAD.
- A request that includes headers other than
Accept
,Accept-Language
orContent-Language
. - A request that has a
Content-Type
header other thanapplication/x-www-form-urlencoded
,multipart/form-data
, ortext/plain
.
Browsers automatically create any necessary preflight requests and send them
before the actual request message. The preflight request is an OPTIONS
request
like the following example:
OPTIONS /data HTTP/1.1
Origin: https://example.com
Access-Control-Request-Method: DELETE
On the server side, the app receiving the request responds to the preflight request with information about the methods the application accepts from this origin:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, DELETE, HEAD, OPTIONS
The server response can also include an Access-Control-Max-Age
header to
specify the duration in seconds to cache preflight results. This allows the
client to send multiple complex requests without needing to repeat the preflight
request.