Projects

Archives

AJAX cross domain requests with CORS

A lot of people (including me a few weeks ago) still think that the same-origin policy of the XMLHttpRequest object makes it impossible to send an AJAX request to a foreign domain, but luckily this isn’t true anymore. Ways like JSONP, Flash bridges or some weird iframe calls (it’s fun – spent a whole night on that aproach ;P) have been found to tackle this issue somehow, but there’s a nice draft from the W3C called Cross-Origin Resource Sharing or short CORS, which solves this problem in a very elegant way and is supported by all major browsers.

The good thing is you don’t need to make any changes on the client side, everything will work like you’re expecting it from a usual XMLHttpRequest, all you’ve got do is to make the server send some “magic” HTTP headers and cross domain requests won’t be a problem no more. And that’s the bad thing as well: Sending the headers shouldn’t be a problem, but it implies that you have server-side access. However, let’s just assume you have access to the server and take a look on a simple example including some PHP and Node.js code snippets.

A simple request

Before we start digging deeper I want to show you the most simple cross domain request (XDR) you could do. As I’ve said nothing specific has to be done on the client side, so it’s just the usual JavaScript code you need for an AJAX request. Since we need different hosts for a cross domain request we assume the following code is part of the page http://www.example.com/dir/page.html:

Normally the browser won’t allow such a request, since we try to communicate with another host, but with the right header this is no problem anymore. Here is a simple example for a possible HTTP response with CORS:

The interesting part is the Access-Control-Allow-Origin header, which defines what origins are allowed to send requests. A star (*) means requests from any origin are allowed, any other value has to be identical to the request origin, otherwise the response will be discarded by the client (aka browser).

But what exactly is the origin of a request? That’s the scheme://host:port part of the URL the request came from (the :port part is optional). In our example the request came from http://www.example.com/dir/page.html, therefore the Access-Control-Allow-Origin value has to be either * or http://www.example.com to be accepted.

Coding the server response

Here are some examples how you could generate the response on the server side. Since this can become as complex as you like I’ll just show some short PHP and Node.js snippets here, but I hope some other guys will give you hints in the comments about how to send the HTTP response in other languages.

PHP: The HTTP response of the example above could be generated by an index.php with the following code:

<?php
header('Access-Control-Allow-Origin: *');
echo 'foobar';
?>

Node.js: A simple HTTP server which always sends a response similar to the one in the example could look like this:

Preflighted requests

The example above will work for GET, HEAD and POST requests without any user-defined headers, but as soon as you have set some additional headers via request.setRequestHeader() or want to use another HTTP method, request.send() will actually result in two requests: a preflight and the real one. The browser will handle the whole preflight stuff for you, so you only have add some extra code on the server side.

A preflight uses two special headers, Access-Control-Request-Method and Access-Control-Request-Headers, to tell the server, what is going to be sent in the actual request. The response contains two similar headers, Access-Control-Allow-Methods and Access-Control-Allow-Headers, to tell the client what methods and headers are allowed for the intended request.

If the preflight fails due to an error (HTTP status 404, 500 or something like that) or non-matching Access-Control-Request and Access-Control-Allow headers, the whole request will fail. However, we’re optimistic, so here is an example of a successful XMLHttpRequest with preflight:

Once again the usual AJAX JavaScript, but this time we’ve send a non-standard header along with our request. Therefore the client will automatically send a preflight before the request and the server has to send an approbate response:

As you can see it is possible to define a list of allowed methods and headers by separating each with a comma. After the preflight has been completed the client will send the actual request and the server has to respond the same way as in the simple request example:

Preflight caching

In order to reduce the network traffic the client won’t send another preflight request as long as it holds a valid preflight-response in its cache. However, this could make debugging somehow difficult, so I recommend to disable caching during development.

This can be achived by sending an Access-Control-Max-Age header along with the preflight-response, which defines how many seconds the response is allowed to stay in the preflight-result-cache of the client. Setting the header value to zero will disable caching.

Some words on the Internet Explorer

Once again our special blue-e’d friend needs some extra care. The Internet Explorer supports CORS since version 8, but alas someone decided not to implement it the standard way, so we can’t use the XMLHttpRequest object, but have to create a XDomainRequest (MSDN page) object instead. The properties and methods of XDomainRequest are very similar to the normal XMLHttpRequest object – except for two things: It won’t allow us to use other methods like GET or POST or to set any user defined headers, so it’s rather useless when you want to communicate with a RESTful API. However, here’s some JavaScript code that should send a cross domain request in any browser that supports CORS: