With the emergence of the Cross Origin Resource Sharing (CORS) specification, now a candidate for W3C Recommendation, web application developers have a browser-supported mechanism to make XmlHttpRequests to another domain in a secure manner.
As of this writing, we can finally say that CORS is supported by all major browsers. It initially appeared in Firefox 3.5, Safari 4, and Chrome 3. Internet Explorer 10 now has native support.
What is a Cross-Origin Request?
If the script on your page is running from domain mydomain.com and would like to request a resource via an XmlHttpRequest or XDomainRequst from domain otherdomain.com, this is a cross-origin request. Historically, for security reasons these types of requests have been prohibited by browsers.
How Does it Work?
The CORS mechanism works by adding HTTP headers to cross-domain HTTP requests and responses. These headers indicate the origin of the request and the server must indicate via headers in the response whether it will serve resources to this origin. This exchange of headers is what makes CORS a secure mechanism. The server must support CORS and indicate that the domain of the client making the request is permitted to do so. The beauty of this mechanism is that it is automatically handled by the browser and web application developers do not need to concern themselves with its details.
For simple cross-site requests (i.e., GETs and POSTs that don’t set custom headers and the request body is plain text or form data), the browser simply includes additional Origin and Referrer headers indicating the requesting domain. For example to retrieve the resource called some-resource at otherdomain.com using the jQuery Ajax API, a developer would simply write [using CoffeeScript for code examples]:
$.get( url: 'http://otherdomain.com/some-resource' ).done successFn
and the browser will issue a request with the following headers:
GET http://otherdomain.com/some-resource/ HTTP/1.1 Referer: http://mydomain.com/myapp/ Origin: http://mydomain.com
Note that the browser will only include the Origin header when the request is cross-origin. A CORS-enabled server receiving this request will include these headers in its response:
Access-Control-Allow-Origin: http://mydomain.com Content-Type: application/json
When the browser sees that the Access-Control-Allow-Origin value matches the domain of the page, it will permit the response to be processed. A server can set a value of “*” in this header to indicate that it is a public resource that allows any origin.
The CORS specification requires browsers to preflight requests that:
- Use request methods other than GET, POST, or HEAD
- Have custom headers
- Have request bodies with Content-Type other than text/plain, application/x-www-form-urlencoded, or multipart/form-data
A preflighted request utilizes the OPTIONS request method to verify that the server is CORS-enabled and supports the type of request the client would like to send. For example, to update the resource called some-resource at otherdomain.com and also set a customer header called X-Foo, a developer would write:
$.ajax( url: 'http://otherdomain.com/some-resource' type: 'PUT' headers: 'X-Foo': 'bar' data: someResource ).done successFn
The browser will first issue a request with the following headers:
OPTIONS http://otherdomain.com/some-resource/ HTTP/1.1 Origin: http://mydomain.com Access-Control-Request-Method: PUT Access-Control-Request-Headers: X-Foo
The CORS-enabled server will respond with response headers indicating that PUT is an allowable request method, X-Foo is an allowable request header, and the results of this preflight request can be cached for 3600 seconds.
HTTP/1.1 200 OK Access-Control-Allow-Origin: http://mydomain.com Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: X-Foo Access-Control-Max-Age: 3600
Then the browser will send the actual PUT request.
PUT http://otherdomain.com/some-resource/ HTTP/1.1 Content-Type: application/xml; charset=UTF-8 X-Foo: bar
The preflight mechanism ensures among other things that servers that are not CORS-enabled will not process a request that might modify server resources as a side effect prior to the browser disallowing the response because it lacks the proper Access-Control-Allow-Origin header.
A browser will not send Cookies or HTTP Auth information in a cross-domain XmlHttpRequst. The client application must indicate that they should be sent by setting the withCredentials property of the XmlHttpRequest or XDomainRequest. By default, this value is false and not set. From the simple request example above, if we wanted to include any cookies that the user may have acquired for domain otherdomain.com along with the request to retrieve the resource called some-resource at otherdomain.com the client would setup its Ajax request as follows:
$.get( url: 'http://otherdomain.com/some-resource' xhrFields: 'withCredentials': true ).done successFn
Note that you must use jQuery 1.5.1+ for this to work as prior versions of jQuery did not propagate the withCredentials property to the native XmlHttpRequest.
The Obligatory Note on Internet Explorer
Internet Explorer 8 and 9 have limited support for CORS. Namely:
- Only GET and POST with a content type of plain/text are supported
- It does not support preflight
- No custom headers may be added to the request
- Credentialed requests are not supported
- Requests must be targeted to the same scheme as the hosting page
Internet Explorer 10 now has native support for CORS. However, as of this writing, IE 10 only supports credentialed requests between domains that have a matching second-level domain name, e.g., a.mydomain.com requesting to b.mydomain.com. It is not yet possible to send credentials between mydomain.com and otherdomain.com.
A great resource for testing CORS requests can be found at test.cors.org. This test site allows you to:
- Send CORS requests to a remote server to verify its capabilities
- Send CORS requests to a test server to explore CORS features
Alternatives to CORS
If your web application must run in browsers that do not support CORS or interact with servers that are not CORS-enabled, there are several alternatives to CORS that have been utilized to solve the cross-origin communication restriction.
Have a comment? Leave it below.