Mozilla

XMLHttpRequest is used within many Ajax libraries, but till the release of browsers such as Firefox 3.5 and Safari 4 has only been usable within the framework of the same-origin policy for JavaScript. This meant that a web application using XMLHttpRequest could only make HTTP requests to the domain it was loaded from, and not to other domains. Developers expressed the desire to safely evolve capabilities such as XMLHttpRequest to make cross-site requests, for better, safer mash-ups within web applications. The Cross-Origin Resource Sharing (CORS) specification consists of a simple header exchange between client-and-server, and is used by IE8′s proprietary XDomainRequest object as well as by XMLHttpRequest in browsers such as Firefox 3.5 and Safari 4 to make cross-site requests. These browsers make it possible to make asynchronous HTTP calls within script to other domains, provided the resources being retrieved are returned with the appropriate CORS headers.

A Quick Overview of CORS

Firefox 3.5 and Safari 4 implement the CORS specification, using XMLHttpRequest as an “API container” that sends and receives the appropriate headers on behalf of the web developer, thus allowing cross-site requests. IE8 implements part of the CORS specification, using XDomainRequest as a similar “API container” for CORS, enabling simple cross-site GET and POST requests. Notably, these browsers send the ORIGIN header, which provides the scheme (http:// or https://) and the domain of the page that is making the cross-site request. Server developers have to ensure that they send the right headers back, notably the Access-Control-Allow-Origin header for the ORIGIN in question (or ” * ” for all domains, if the resource is public) .

The CORS standard works by adding new HTTP headers that allow servers to serve resources to permitted origin domains. Browsers support these headers and enforce the restrictions they establish. Additionally, for HTTP request methods that can cause side-effects on user data (in particular, for HTTP methods other than GET, or for POST usage with certain MIME types), the specification mandates that browsers “preflight” the request, soliciting supported methods from the server with an HTTP OPTIONS request header, and then, upon “approval” from the server, sending the actual request with the actual HTTP request method. Servers can also notify clients whether “credentials” (including Cookies and HTTP Authentication data) should be sent with requests.

Capability Detection

XMLHttpRequest can make cross-site requests in Firefox 3.5 and in Safari 4; cross-site requests in previous versions of these browsers will fail. It is always possible to try to initiate the cross-site request first, and if it fails, to conclude that the browser in question cannot handle cross-site requests from XMLHttpRequest (based on handling failure conditions or exceptions, e.g. not getting a 200 status code back). In Firefox 3.5 and Safari 4, a cross-site XMLHttpRequest will not successfully obtain the resource if the server doesn’t provide the appropriate CORS headers (notably the Access-Control-Allow-Origin header) back with the resource, although the request will go through. And in older browsers, an attempt to make a cross-site XMLHttpRequest will simply fail (a request won’t be sent at all).

Both Safari 4 and Firefox 3.5 provide the withCredentials property on XMLHttpRequest in keeping with the emerging XMLHttpRequest Level 2 specification, and this can be used to detect an XMLHttpRequest object that implements CORS (and thus allows cross-site requests). This allows for a convenient “object detection” mechanism:

if (XMLHttpRequest)
{
    var request = new XMLHttpRequest();
    if (request.withCredentials !== undefined)
    {
      // make cross-site requests
    }
}

Alternatively, you can also use the “in” operator:

if("withCredentials" in request)
{
  // make cross-site requests
}

Thus, the withCredentials property can be used in the context of capability detection. We’ll discuss the use of “withCredentials” as a means to send Cookies and HTTP-Auth data to sites later on in this article.

“Simple” Requests using GET or POST

IE8, Safari 4, and Firefox 3.5 allow simple GET and POST cross-site requests. “Simple” requests don’t set custom headers, and the request body only uses plain text (namely, the text/plain Content-Type).

Let us assume the following code snippet is served from a page on site http://foo.example and is making a call to http://bar.other:


var url = "http://bar.other/publicNotaries/"
if(XMLHttpRequest)
{
  var request = new XMLHttpRequest();
  if("withCredentials" in request)
  {
   // Firefox 3.5 and Safari 4
   request.open('GET', url, true);
   request.onreadystatechange = handler;
   request.send();
  }
  else if (XDomainRequest)
  {
   // IE8
   var xdr = new XDomainRequest();
   xdr.open("get", url);
   xdr.send();

   // handle XDR responses -- not shown here :-)
  }

 // This version of XHR does not support CORS
 // Handle accordingly
}

Firefox 3.5, IE8, and Safari 4 take care of sending and receiving the right headers. Here is the Simple Request example. It is also instructive to look at the headers sent back by the server. Notably, amongst the other request headers, the browser would send the following in order to enable the simple request above:

GET /publicNotaries/ HTTP/1.1
Referer: http://foo.example/notary-mashup/
Origin: http://foo.example

Note the use of the “Origin” HTTP header that is part of the CORS specification.

And, amongst the other response headers, the server at http://bar.other would include:

Access-Control-Allow-Origin: http://foo.example
Content-Type: application/xml
......

A more complete treatment of CORS and XMLHttpRequest can be found here, on the Mozilla Developer Wiki.

“Preflighted” Request

The CORS specification mandates that requests that use methods other than POST or GET, or that use custom headers, or request bodies other than text/plain, are preflighted. A preflighted request first sends the OPTIONS header to the resource on the other domain, to check and see if the actual request is safe to send. This capability is currently not supported by IE8′s XDomainRequest object, but is supported by Firefox 3.5 and Safari 4 with XMLHttpRequest. The web developer does not need to worry about the mechanics of preflighting, since the implementation handles that.

The code snippet below shows code from a web page on http://foo.example calling a resource on http://bar.other. For simplicity, we leave out the section on object and capability detection, since we’ve covered that already:

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '
Arun';
function callOtherDomain(){
if(invocation)
{
    invocation.open('POST', url, true);
    invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
    invocation.setRequestHeader('Content-Type', 'application/xml');
    invocation.onreadystatechange = handler;
    invocation.send(body);
}

You can see this example in action here. Looking at the header exchange between client and server is really instructive. A more detailed treatment of this can be found on the Mozilla Developer Wiki.

In this case, before Firefox 3.5 sends the request, it first uses the OPTIONS header:

OPTIONS /resources/post-here/ HTTP/1.1
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER

Then, amongst the other response headers, the server responds with:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://arunranga.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER
Access-Control-Max-Age: 1728000

At which point, the actual response is sent:

POST /resources/post-here/ HTTP/1.1
...
Content-Type: application/xml; charset=UTF-8
X-PINGOTHER: pingpong
...

Credentialed Requests

By default, “credentials” such as Cookies and HTTP Auth information are not sent in cross-site requests using XMLHttpRequest. In order to send them, you have to set the withCredentials property of the XMLHttpRequest object. This is a new property introduced in Firefox 3.5 and Safari 4. IE8′s XDomainRequest object does not have this capability.

Again, let us assume some JavaScript on a page on http://foo.example wishes to call a resource on http://bar.other and send Cookies with the request, such that the response is cognizant of Cookies the user may have acquired.

var request = new XMLHttpRequest();
var url = 'http://bar.other/resources/credentialed-content/';
function callOtherDomain(){
  if(request)
  {
   request.open('GET', url, true);
   request.withCredentials = "true";
   request.onreadystatechange = handler;
   request.send();
  }

Note that withCredentials is false (and NOT set) by default. The header exchange is similar to the case of of a simple GET request, with the exception that now an HTTP Cookie header is sent with the request header. You can see this sample in action here.

A Note on Security

In general, data requested from a remote site should be treated as untrusted. Executing JavaScript code retrieved from a third-party site without first determining its validity is NOT recommended. Server administrators should be careful about leaking private data, and should judiciously determine that resources can be called in a cross-site manner.

References

46 comments

Comments are now closed.

  1. William Edney wrote on July 6th, 2009 at 4:41 pm:

    Arun -

    Thanks for the excellent example. I grabbed the ‘Simple Example’ page, saved it to my file system, reloaded that page into another window using the ‘file:///’ URL and tried to invoke the cross-site query. This failed in both Firefox 3.5 (Mac) and Safari 4 (Mac).

    Is there some reason this isn’t working? Given that the W3 defines ‘file:///’ URLs as a valid origin, this should work IMHO.

    Cheers,

    - Bill

  2. William Edney wrote on July 6th, 2009 at 4:48 pm:

    Arun -

    Ok, so maybe I’m being an idiot and your server is only authorizing the domain that you’re calling from in your example and not ‘*’.

    Hmmm…

    Cheers,

    - Bill

  3. thinsoldier wrote on July 6th, 2009 at 5:05 pm:

    Been waiting all day an update!

    :) thanks.

  4. Jeff Walden wrote on July 6th, 2009 at 7:38 pm:

    Typo: “Cross-Origin Resource Sharing”, not “request” sharing.

  5. Gen Kanai wrote on July 6th, 2009 at 8:55 pm:

    @Jeff- thanks for the edit. Updated.

  6. Arun Ranganathan wrote on July 6th, 2009 at 11:15 pm:

    @Bill — good question :) What’s happening when you take the simple request and run it locally (from file:///) is that the value of the Origin header is now null (“Origin: null”). But, my server-side PHP script doesn’t handle a null Origin and thus doesn’t send back the right response.

    @Jeff — yikes, good catch :)

  7. Firefox Fanatic wrote on July 7th, 2009 at 8:35 am:

    So, Firefox 3.5, Safari 4, and Google Chrome 2 all support the CORS specification, while Internet Explorer 8 partially supports it with XDomainRequest. What about Opera? Is there any news on when they will support this functionality?

  8. Arun Ranganathan wrote on July 7th, 2009 at 9:42 am:

    @FirefoxFanatic — no comment from Opera yet; the last public-facing message we got from an Opera engineer was: http://lists.w3.org/Archives/Public/public-webapps/2009AprJun/1223.html

  9. William Edney wrote on July 7th, 2009 at 10:53 am:

    Arun -

    Did a bit more sussing of what’s going here.

    1. I’m an idiot and only after posting did I figure out that your server wasn’t configured with ‘Access-Control-Allow-Origin: *’.

    2. I found a test server that does:

    http://rockstarapps.com/test.php.

    3. Tested both FF 3.5 and Safari 4.X against that server. It turns out that Safari 4.X works properly, FF 3.5 does not. Off to Bugzilla…

    Thanks!

    Cheers,

    - Bill

  10. William Edney wrote on July 7th, 2009 at 11:23 am:

    Arun -

    One last message. In reducing this for a testcase for FF 3.5, I found an error in my previous test. FF 3.5 works fine. Haven’t tried this in IE8, yet :-)

    Thanks again for these helpful examples :-).

    Cheers,

    - Bill

  11. Pingback from links for 2009-07-08 | 小李贼 on July 8th, 2009 at 6:30 am:

    [...] cross-site xmlhttprequest with CORS 站点间的 xmlhttp 交互 (tags: javascript ajax) [...]

  12. Pingback from Ajax cross-domain con Mozilla 3.5 y Safari 4 | aNieto2K on July 8th, 2009 at 11:06 pm:

    [...] y ahora Firefox 3.5, ya implementan dicha mejora y nos permite trabajar con ella. Por otro lado Microsoft, en otro mundo, desarrolla XDomainRequest() que permite realizar [...]

  13. Pingback from Ajax cross-domain con Mozilla 3.5 y Safari 4 : Blogografia on July 8th, 2009 at 11:20 pm:

    [...] y ahora Firefox 3.5, ya implementan dicha mejora y nos permite trabajar con ella. Por otro lado Microsoft, en otro mundo, desarrolla XDomainRequest() que permite realizar [...]

  14. Pingback from Ajax cross-domain con Safari 4, Google Chrome 2 y Firefox 3.5 : Blogografia on July 9th, 2009 at 12:01 am:

    [...] Google Chrome 2 y ahora Firefox 3.5, ya implementan dicha mejora y nos permite trabajar con ella. Por otro lado Microsoft, en otro mundo, desarrolla XDomainRequest() que permite realizar [...]

  15. Pingback from El Blog Mojaina » Ajax cross-domain con Safari 4, Google Chrome 2 y Firefox 3.5 on July 9th, 2009 at 2:54 am:

    [...] permitiendo una mejor integración entre servicios online. Safari4, Google Chrome 2 y ahora Firefox 3.5, ya implementan dicha mejora y nos permite trabajar con ella. Por otro lado Microsoft, en otro mundo, desarrolla XDomainRequest() que permite realizar [...]

  16. Pingback from securitybananas.com » XMLHTTPReqest “Ping” Sweeping in Firefox 3.5+ on July 20th, 2009 at 12:07 pm:

    [...] brought my attention to the new Firefox 3.5+ CORS (Cross-Origin Resource Sharing) which is a way to do a cross domain XMLHTTPReqest. Does that sound scary? Well, it is, but [...]

  17. Pingback from Chris B. – idéias e pensamentos » XMLHttpRequest cross-site: mais uma vez o IE fora do padrão on August 27th, 2009 at 2:31 pm:

    [...] não é falar da implementação em si, até porque eu não conseguiri escrever nada melhor do que foi escrito pelo hacks.mozilla, mas sim falar que novamente a Microsoft atrapalha os padrões (aliás, vejam com cuidado esse post [...]

  18. Xiaoxin wrote on August 31st, 2009 at 9:01 am:

    Tested CORS with Chrome and it works however xhr.withCredentials always comes back undefined making this feature detection method unrealiable. Tested on Chrome 2.0.172.43.

  19. Pingback from 5 years of Firefox at hacks.mozilla.org on November 9th, 2009 at 2:53 am:

    [...] One thing that’s become obvious over the last five years is the wide gap that’s emerging between the field of modern browsers – Firefox, Safari, Opera and Chrome – with the world’s most popular browser – IE. The modern browser is built for the future of web applications – super fast JavaScript, modern CSS, HTML5, support for the various web-apps standards, downloadable font support, offline application support, raw graphics through canvas and WebGL, native video, advanced XHR capabilities mixed with new security tools and network capabilities. [...]

  20. Pingback from Cross Origin Resource Sharing – AKA The Holy Grail | Brit Gardner ::: Web Developer ::: Dallas, TX on December 2nd, 2009 at 7:05 am:

    [...] I stumbled across this article on the excellent Mozilla Hacks blog. Cross Origin Resource Sharing (CORS). Sweet! Finally a true [...]

  21. Pingback from Cross-domain Ajax with Cross-Origin Resource Sharing | NCZOnline on May 25th, 2010 at 6:37 pm:

    [...] Mozilla team suggests in their post about CORS that you should check for the existence of the withCredentials property to determine if the browser [...]

  22. Nathan Friedly wrote on May 26th, 2010 at 1:50 pm:

    I always get back null or “” when calling getAllResponseHeaders() & getResponseHeader() on cross-domain requests. Why is that and how can I read the headers?

  23. Pingback from HTML5 기반 오프라인 이미지 에디터 데모 ✩ Mozilla 웹 기술 블로그 on July 20th, 2010 at 6:41 am:

    [...] Cross-Origin Resource Sharing 어플리케이션을 하나의 사이트에서 제공하며 데이터를 다른 곳으로 출판할 수 있다. [...]

  24. WebDAV wrote on July 29th, 2010 at 8:43 am:

    We have tested CORS in Firefox 3.6, Chrome 5 and Safari 5 and found that only Chrome can handle requests to servers with authentication properly. We have tested cross-domain PROPFIND request with Basic, Digest and NTLM and found that Firefox supports only Digest authentication (for PROPFIND it does not support Basic even with SSL for some reason) while Safari does not support any authentication for PROPFIND requests at all.

    It is a great disappointment as PROPFIND and other WebDAV verbs are critical for our product, hope they will fix it.

    We have published the results here: http://www.webdavsystem.com/ajaxfilebrowser/programming/cross_domain. See Cross-Domain Requests with Authentication section at the bottom of the page.

    1. Christopher Blizzard wrote on August 16th, 2010 at 2:59 pm:

      Do you have a test case for this? It should work.

      1. Vladimir Lichman wrote on September 16th, 2010 at 7:34 pm:

        Christopher, we have posted a bug here:
        https://bugzilla.mozilla.org/show_bug.cgi?id=597301

        There is a detailed description about how to reproduce it.

  25. Intekhab wrote on November 16th, 2010 at 10:30 am:

    Hi Arun,

    Your article is very helpful to understand the concept of Cross domain calling. I tried out the same but when i call a web service (WCF with webHttpbinding) hosted on other machineanother wb site i got an error 403 forbidden with status 0 and ready state 4.

    Actual scenario:
    WCF with Httpbinding
    Ajax call using XMLHTTP object
    POST method
    Preflight request as content type is application/Json

    Any help is appriciated

    Intekhab

  26. kn33ch41 wrote on December 15th, 2010 at 6:13 am:

    If anyone is having trouble sending cookies with withCredentials, remember that Access-Control-Allow-Origin must have a valid domain specified that corresponds to those cookies; a wildcard will not work.

    Also, for anyone sending files asynchronously with XHR2, bear in mind that Chrome sets a Content-Type header by default when sending a base64-encoded stream, for example, which must be specified as an allowed header in the server’s preflighted Access-Control-Allow-Headers response. Firefox, however, does not need this specified.

  27. Nizzy wrote on December 30th, 2010 at 12:44 am:

    With CORS, why getAllResponseHeaders() return null?

    any idea?

    thank you
    Nizzy

  28. Pingback from MS Visual Studio 2010 » Blog Archive » Подборка ссылок. 05.01.2011 on January 27th, 2011 at 3:31 am:

    [...] Robust Software : Cross-site XMLHttpRequest with CORS ; [...]

  29. Demetris wrote on January 29th, 2011 at 8:13 am:

    Your said “The web developer does not need to worry about the mechanics of preflighting, since the implementation handles that”. Are you referring to the client side (the browser) that automatically generates the preflight request? I think so. Is this also always true about the server? I do know Jetty has a configuration to handle preflight requests but most other cases i have been the preflight response is handled by a user defined servlet. What do you think?

    Thanks

  30. Demetris wrote on January 29th, 2011 at 8:18 am:

    Also – I intercepted the CORS preflight request with a local agent, inspected the OPTIONS headers and then returned the response as it should be (headers to allow the origin etc. with rn terminating them). Nothing happens on the browser – why is that the case? Using Chrome on Android.

  31. Pingback from Apache: proxy POST requests to remote server, handle OPTIONS requests locally | DEEP in PHP on February 14th, 2011 at 10:24 am:

    [...] trying to configure Apache to act as a proxy to a remote server, to allow cross-domain AJAX using CORS. To achieve this, I need Apache to respond to 2 HTTP verbs, like [...]

  32. Pingback from thyncology : Linkfest I on April 10th, 2011 at 3:52 am:

    [...] CORS Have started working on mobile stuff at work (via PhoneGap Build and Jo) and recently started using XHR for login within the app. PhoneGap enables this somehow via CORS (this is my understanding, please correct if wrong) which allows for Cross Origin Resource Sharing through the exchange of headers listing trusted origins etc. Really just got a brief understanding of it out of curiosity. [...]

  33. Pingback from REST Communication with Ajax jQuery: a Bumpy Road | rassembling bits on May 20th, 2011 at 3:57 pm:

    [...] you don’t care about some browsers (i.e. != Firefox 3.5, Safari 4, Chrome 2), you could add a CORS response header in the form of Access-Control-Allow-Origin: *. But then again, if you have control [...]

  34. Totti Anh Nguyen wrote on May 25th, 2011 at 3:39 am:

    Thanks for the clear Javascript sample snippet to demo the feature !

  35. Pingback from Use ASP.NET’s HttpHandler to bridge the cross-domain gap - Encosia on August 2nd, 2011 at 8:03 am:

    [...] an API with no JSONP support, the cross-domain barrier can quickly become a formidable one. CORS is slowly becoming a viable alternative, but it requires that the remote service support it via [...]

  36. Max wrote on October 19th, 2011 at 11:34 am:

    Thanks for the info! This may be what I’m looking for:

    I have an HTTP page that needs to perform and AJAX POST to a secure url. Both on the same domain.
    Will CORS allow me to do that?

    I also have total control on the JS that is loaded by the page, so I can even host the JS files in a secure (HTTPS) environment too.

    What do you think?

  37. santhosh kumar wrote on October 19th, 2011 at 4:50 pm:

    I face the same cross domain issue… I am sure whether I am setting the header properly

    http://stackoverflow.com/questions/7747695/cross-domain-issue-xmlhttp

    Am I missing something?

  38. Zain Shaikh wrote on December 2nd, 2011 at 6:22 am:

    I tried the sample provided in firefox 3.0.1.
    http://arunranga.com/examples/access-control/preflightInvocation.html

    but I am getting this error:

    Access to restricted URI denied” code: “1012

    any solution???

  39. Pingback from E-sitesweb » COR Blimey! Cross domain Ajax is really here on December 14th, 2011 at 6:59 am:

    [...] Mozilla team suggests in their post about CORS that you should check for the existence of the withCredentials property to determine if the browser [...]

  40. Ranjan wrote on June 20th, 2012 at 6:19 am:

    Hi Arun,

    I tried your sample file, it works fine.
    However, when I try the same with different URL on our server ,it doesn’t work.

    Could you please tell me why it is not working.

    Ranjan

  41. Kristoffer S wrote on October 10th, 2012 at 1:44 am:

    This one saved my day. Thanks!

  42. kishore wrote on October 29th, 2012 at 3:21 am:

    When i use Cross Domain XMLHTTP request, it works find in Fire Fox. But the same code shows 403 Forbidden in chrome.
    Can any body please suggest me how to resolve this issue ?

  43. Hridya wrote on March 7th, 2013 at 12:09 am:

    Hi,

    I tried your code to hit my webservice. It worked in chrome and IE.

    Bur Firefox gave me a 405 Method Not Allowed error.

    Can you please help me on this?

    Thanks in advance

  44. Nathan Friedly wrote on March 7th, 2013 at 4:24 pm:

    Hridya: That probably means Firefox is preflighting your requestion with an OPTIONS request and your web server doesn’t support those. You can remove the preflighting by not adding cookies (withCredentials=false) and not setting any headers.

Comments are closed for this article.