CORS for XHR in IE10

The fourth platform of IE10 simplifies building cross-site scenarios that work consistently across browsers by supporting Cross-Origin Resource Sharing (CORS) for XMLHttpRequest (XHR). CORS for XHR makes sharing data across sites simple and flexible. In the most basic scenario CORS enables creating data sources accessible from any site, and with a few small tweaks you can choose to constrain allowed sites, support data modification, and even allow authentication. Most importantly CORS keeps existing sites secure by requiring server participation.

Simple Cross-Origin XHR

Let’s look at how a cross-origin XHR request compares to a same-origin request. From script, the only difference is the URL passed to the open method. For example, say we’re working on a script that fetches a list of photo albums.

Traditional XHR

// Script running on https://photos.contoso.com

var xhr = new XMLHttpRequest();

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("GET", "/albums", true);

xhr.send();

Now we want to access the list of albums from another origin. The other origin can be a completely different domain or a different host with the same base domain. Either way, just pointing at the full URL from another site is enough to get the browser to automatically send a CORS request.

CORS-Enabled XHR

// Script running on https://www.contoso.com

var xhr = new XMLHttpRequest();

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("GET", "https://photos.contoso.com/albums", true);

xhr.send();

Sites can provide fallback for older browsers by wrapping this in feature detection. Checking for “withCredentials” is the best approach since it directly relates to CORS support for XHR.

CORS-Enabled XHR with Feature Detection

// Script running on https://www.contoso.com

var xhr = new XMLHttpRequest();

if ("withCredentials" in xhr) {

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("GET", "https://photos.contoso.com/albums", true);

xhr.send();

} else {

// Fallback behavior for browsers without CORS for XHR

}

At this point our client code makes a CORS request directly to "https://photos.contoso.com", but the request fails to return any data. The failure occurs because the server isn’t participating yet. Taking a quick look at the developer tools gives us an idea what went wrong.

Screenshot showing the F12 tools indicating no 'Access-Control-Allow-Origin' header was found.

Here we can see the server needs to send an “Access-Control-Allow-Origin” header in the response. In our scenario we’re not opening up our albums for any site to access, but want to enable access solely from “https://www.contoso.com”. Doing this requires allowing the server to identify where the request originated. Examining our outgoing request reveals a new header containing precisely this information, “Origin”.

Simple CORS Request Headers

GET https://photos.contoso.com/albums HTTP/1.1

Origin: https://www.contoso.com

...

Using this information the server can choose to limit access to any set of sites. If the server always adds an “Access-Control-Allow-Origin” header with a value of '*' then all sites will have access. For our scenario, we’ll have the server verify the origin and then set “Access-Control-Allow-Origin” to allow only “https://www.contoso.com”.

Simple CORS Response Headers

HTTP/1.1 200 OK

Access-Control-Allow-Origin: https://www.contoso.com

...

With the above updates in place, our “https://www.contoso.com” client can now access album lists from the server at “https://photos.contoso.com”.

Cross-Origin XHR with Preflight

The “simple” CORS requests discussed so far are great for basic, read-only scenarios, like downloading a photo album. Taking the next step by modifying data across sites requires a bit more work on the server. For example, say we’re adding code in the client to create a new album.

var xhr = new XMLHttpRequest();

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("PUT", "https://photos.contoso.com/albums", true);

xhr.send(JSON.stringify({ name: "New Album" }));

Running this as-is doesn’t work. Examining the network traffic reveals a request is sent, but not the one we expected.

Screenshot of the F12 tools showing an OPTIONS preflight request.

What the browser actually sent is known as a preflight request. Preflight requests are sent before requests that may result in data modification on the server. Such requests are identified by the presence of non-simple properties as defined in the CORS specification. These properties range from certain HTTP methods like “PUT” to custom HTTP headers. Browsers send preflight requests to ask the server for permission to send the actual request. In our example the browser is verifying a “PUT” request is allowed.

Preflight Request

OPTIONS https://photos.contoso.com/albums HTTP/1.1

Origin: https://www.contoso.com

Access-Control-Request-Method: PUT

...

Getting the browser to send the actual request requires some changes on the server. Once again we can take a look at the developer tools for more information.

Screenshot showing the F12 tools indicating no 'Access-Control-Allow-Methods' list was found.

The first step is to make the server recognize the “OPTIONS” preflight request as distinct from other requests for the same URL. After the server verifies the preflight request by ensuring “Access-Control-Request-Method” is asking for “PUT” from an allowed origin, it sends the appropriate approval via the “Access-Control-Allow-Methods” header.

Preflight Response

HTTP/1.1 200 OK

Access-Control-Allow-Origin: https://www.contoso.com

Access-Control-Allow-Methods: PUT

...

Once preflight is out of the way and approved the actual request takes place.

Actual Request

PUT https://photos.contoso.com/albums HTTP/1.1

Origin: https://www.contoso.com

...

Adding the album is technically complete at this point, but our client code won’t know that unless the server responds correctly. Specifically the server must still include “Access-Control-Allow-Origin” in the response.

Actual Response

HTTP/1.1 200 OK

Access-Control-Allow-Origin: https://www.contoso.com

...

With that the client can add a new album cross-origin and recognize whether or not the action completed successfully.

Next Steps

Pairing CORS with other new platform features enables interesting scenarios. One example is the Cross-Site Upload Test Drive which tracks cross-origin file uploads using CORS, XHR, FileAPI, and progress events.

—Tony Ross, Program Manager, Internet Explorer

Comments

  • Anonymous
    February 09, 2012
    It's probably worth mentioning that this is intended as a safer and more flexible replacement for things like JSONP, and that on IE9 XDomainRequest can be used as a fallback.

  • Anonymous
    February 09, 2012
    The comment has been removed

  • Anonymous
    February 09, 2012
    @György Balássy : At this point, this is already the case. You can build a <FORM> and send it into an iframe. That let you customize posted content and URL, but you can't retreive data about the result if the website doesn't allow it. Nothing has changed, it's just more convenient to do so.

  • Anonymous
    February 10, 2012
    Great to see CORS support. I couldn't tell, are you also supporting CORS for webfonts, as required by the CSS3  Fonts specification?

  • Anonymous
    February 10, 2012
    Will IE10 be available for Windows 7 and Windows Vista systems? And will we ever see a downloadable developer preview outside Win8 betas again?

  • Anonymous
    February 12, 2012
    yes when is it IE10 coming windows 7??

  • Anonymous
    February 12, 2012
    @Release Plan, it would be nice if people would actually read the blog before posting. in short yes for windows 7 no for vista. If I remember right next release will be the beta

  • Anonymous
    February 12, 2012
    Sometimes if the system shuts unexpectedly, on next boot IE9 forgets all the preferences. These include:

  1. The :visited hyperlinks, the popular sites on about:Tabs page.. though the history (Ctrl+H) and that of onebar (Ctrl+E) remains intact
  2. Bing language preference.
  3. Forgets the saved password for Hotmail / Window-Live ID but remembers that of Gmail !
  4. Forgets the autocomplete fields' value. and so on.. Please prevent IE10 from this disaster!
  • Anonymous
    February 13, 2012
    Excited to hear that IE10 will support the standard XHR object as opposed to the proprietary XDR. Question -- will IE10 also do away with the "same scheme policy"? Every browser but IE currently allows CORS to work regardless of the protocol of the origin page and the request URI. In IE, the protocol must match. There are many valid use cases where a page served over HTTP needs to request a URL over HTTPS, but with the current implementation of CORS and XDR, this is not possible.