News Archive
PhpRiot Newsletter
Your Email Address:

More information

Cross-Origin Ajax with CORS

Note: This article was originally published at Planet PHP on 15 December 2011.
Planet PHP

There's no doubt that Ajax is one of the most exciting, useful, and necessary web technologies available to front-end developers. Unfortunately, it's also one of the most restrictivea-aespecially when it comes to gathering content from other domains. Web developers are nothing if not persistent, so we've come up with a variety of ways to get around cross-origin restrictions, including JSONP, server-side proxies made with PHP, ProxyPass proxying, Flash transports, creative iFrame uses, and more. What many developers don't know is that there's a W3C specification called Cross-Origin Resource Sharing, or CORS, which provides a standard for cross-origin Ajax requests with minimal hassle.

Since the Ajax API is different across browsers, and most developers use a JavaScript toolkit, examples within this post will use the MooTools JavaScript framework. CORS will work with any JavaScript framework, since the core philosophy and code is configured on the server, not the client side. Due to its popularity, Apache will be used in server-side configuration examples.

Basic Ajax Requests

Each of the JavaScript frameworks abstracts XMLHttpRequest objects (or in the case of Internet Explorer, ActiveXObject or Microsoft.XMLHttp) to make Ajax requests. These requests feature a URL and may contain extra headers, data, different request types (GET, POST, PUT, or DELETE), and much more. A basic Ajax request would look something like this:

// Create a new Ajax request var request = new Request.JSON({ // The URL to get content from url: "/countries.json", // The success callback onSuccess: function(countries) { // Log out the content console.log("The countries are: ", countries); } }).send(); // Send the request

The URL within the request above is local; countries.json is located within the same origin. What if we try to get tweets from Twitter, though?

// Create a new Ajax request var request = new Request.JSON({ // The URL to get content from url: "", // The success callback onSuccess: function(tweets) { // Log out the content console.log("The tweets are: ", content); } }).send(); // Send the request

The request to Twitter will fail because the request destination,, is not the same as the origin. Each browser provides its own error message; Chrome will warn you with: XMLHttpRequest cannot load Origin is not allowed by Access-Control-Allow-Origin.

In the case of Twitter, we could use a JSONP request instead, but the problem with JSONP is that the destination server must support it. Even if the destination supports JSONP, you cannot POST to the URL or send specified request headers. How do we fix this conundrum? CORS, of course.

Implementing CORS

CORS allows for cross-origin requests with little fuss. Since the destination server is the entity in control and aoat risk,a it must be configured with the proper headers and security settings. A few of the key headers include:

Access-Control-Allow-Origin A specific URI or * which identifies what domain(s) may make cross-origin requests to this (destination) server. (* is an undesirable configuration value, since it allows any and all origins to make requests to your server.) Access-Control-Allow-Methods A comma-separated list of allowed request methods. Access-Control-Allow-Headers A comma-separated list of allowed request headers.

Assuming that a web site is hosted on an Apache server, the virtual host could be configured as follows:

DocumentRoot "/path/to/website/root" ServerName domain.tld Header set Access-Control-Allow-Origin Header set Access-Control-Allow-Methods POST,GET Header set Access-Control-Allow-Headers X-Authorization,X-Requested-With

The configuration above only allows remote requests from, the request type may only be POST or GET, and allowed headers are X-Authorization and X-Requested-With.

If you want to allow anyone to make Ajax requests to your domain and with any request type, you could opt for this configuration:


Truncated by Planet PHP, read more at the original (another 1795 bytes)