Enable cross-domain, cross-browser AJAX/JSON calls using jQuery

13 July, 2013 by Tom Elliott

This post explains how to make a simple, cross-domain, cross-browser JSON call to a script on a different domain.

For security reasons, browsers cannot make GET or POST calls to scripts on other domains using JavaScript – which is a good thing – but means us web developers have to jump through a couple of hoops whenever we need to make JavaScript based cross-domain requests. There are many valid reasons why we might want to call a script from another domain, for example requesting an authenticated twitter stream in JSON format.

To enable cross-domain JSON requests or AJAX calls usually requires 2 steps. First, you must instruct the target server from where the script is being requested from that it’s OK to accept calls from other domains. We do this by modifying the HTTP response header. Secondly, we need to use Microsoft ‘XDR’ (Cross-Domain Request) in our JavaScript JSON request so that our cross-domain request is compatible in Internet Explorer 8 and 9. Modern browsers Chrome, FireFox, Safari and Internet Explorer 10 use a cross domain standard called ‘CORS’ (Cross Origin Resource Standard) rather than XDR, so a regular $.getJSON or $.ajax call here will work fine.

Step 1 – Modifying the HTTP response header

First we need to add the command ‘Access-Control-Allow-Origin’ to our HTTP headers on our target server. This fixes common cross-domain errors that look similar to:
XMLHttpRequest cannot load http://www.targetdomain.com/filename.txt Origin http://www.sourcedomain.com is not allowed by Access-Control-Allow-Origin

The examples below are using the .htaccess file on an Apache/Linux setup but the principles are the same in other server environments. The same techniques can be implemented in your web.config file, or direct in a PHP or .NET file (if these scripts generate the JSON output direct).

The following command in the .htaccess file will modify the response headers for all incoming requests to any scripts within the folder (and all child folders) where the .htaccess file is placed.

<IfModule mod_headers.c>
   Header add Access-Control-Allow-Origin: *
</IfModule>

This has some security implications however, as you’re allowing a potential open door to cross site scripting access for any incoming domain. A much better solution would be to specify the specific domain(s) or “origins” that will be accessing the script.

The below command specifies the HTTP response for a single domain only.

<IfModule mod_headers.c>
   Header add Access-Control-Allow-Origin: "http://www.sourcedomain.com"
</IfModule>

If you want to give access to multiple domains, we can use Regex as below, separating our domains with the vertical line “|”

<IfModule mod_headers.c>
   SetEnvIf Origin "http(s)?://(www.)?(domain1.com|domain2.com)$" AccessControlAllowOrigin=$0$1
   Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
</IfModule>

Once you’ve created or modified the .htaccess file, upload it into the folder where the JSON script or file that you want to access is located.

Step 2 – using XDR to make a cross-domain request

You may already be making a JSON request or AJAX call similar to the below. Now that we’ve made the change to the .htaccess file, this request should now work in Chrome, FireFox, Safari and Internet Explorer 10.

In the example below I’m using a basic jQuery $.getJSON command with ‘GET’ but the same principles should apply to $.ajax and POST commands.

var url = "http://www.targetdomain.com/path-to-script/ouput-json.php";

$.getJSON(url, function(data) {
	mainfunction(data);
})

function mainfunction(data) {
       //console.log(data);
       //Your main cross domain function here.
}

What we now need to do is add additional commands for an XDR request as shown below, so that our JSON/AJAX call is compatible in IE 8 and IE 9. The first line detects if the version of Internet Explorer is older than IE 10 and if so, initiates the XDR, opens a connection, sends the request and then runs our function once the response has been loaded and parsed as JSON data.

var url = "http://www.targetdomain.com/path-to-script/ouput-json.php";
var browser = navigator.userAgent;
var IEversion = 99; //Give a default value for non-IE browsers

if (browser.indexOf("MSIE") > 1) { //Detects if IE
	IEversion = parseInt(browser.substr(browser.indexOf("MSIE")+5, 5));
}
if (IEversion < 10) { 				
	xdr = new XDomainRequest();   // Creates a new XDR object.
	xdr.open("GET", url); // Creates a cross-domain connection with our target server using GET method. 
	xdr.send(); //Send string data to server
	xdr.onload = function () { //After load, parse data returned by xdr.responseText			
		mainfunction($.parseJSON(xdr.responseText));
	};				
} else {
	$.getJSON(url, function(data) {
		mainfunction(data);
	})
}

function mainfunction(data) {
       //console.log(data);
       //Your main cross domain function here.
}

Test your cross-domain JSON script. Hopefully it should now all work cross-domain in all modern browsers and Internet Explorer 8 and 9.

Main References
http://stackoverflow.com/questions/1653308/access-control-allow-origin-multiple-origin-domains
http://msdn.microsoft.com/en-us/library/dd573303(v=vs.85).aspx

Note: This cross-domain JSON solution will not work in Internet Explorer 7 and below
Update: Replaced the now depreciated $.browser detection in jQuery with basic IE version detection



10 Comments

  • Emanuela says:

    Hi,
    I need to retrieve a json object with an http request, so I used this approach:

    $.getJSON(url, function(data) {
    mainfunction(data);
    })

    function mainfunction(data) {
    $(“#content1”).html(JSON.stringify(data));
    $(“#content2”).html(data[1].id);
    }

    the server replies correctly, but the browser (Firefox) returns the error: “SyntaxError: invalid label” in the response of the server and it doesn’t show the response.

    my data are structured as follow:

    {
    “1”:{“id”:”1″,”jobId”:”0″,”jobName”:”biegebalken”},
    “2”:{“id”:”2″,”jobId”:”0″,”jobName”:”biegebalken2″}
    }

    Please, could you help me?

  • Sachin says:

    Thanks for your post, its really helpful. However I have doubt about the POST request. What if I want to set some custom headers in my POST call? As I read from the article it XDomainRequest does not support setting request headers. Here is the link to article – http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx

  • ninioe says:

    You can use Ajax-cross-origin a jQuery plugin. With this plugin you use jQuery.ajax() cross domain.

    It is very simple to use:

    $.ajax({
    crossOrigin: true,
    url: url,
    success: function(data) {
    console.log(data);
    }
    });

    You can read more here: http://www.ajax-cross-origin.com/

  • Raghu Veer says:

    thank you, the solution is helpful especially to deal the current situation using .htaccess file 🙂

  • Great article Tom, thanks!

    By default, cross-origin requests do not provide credentials (for example cookies). So if you need to send them alongside the request you should provide withCredentials property. See how: http://zinoui.com/blog/cross-domain-ajax-request

  • roja says:

    hi
    i tried to get json object with ajax get() then I am getting cross domain error for resolving this one i have changed datatype as jsonp error resolved but again i am getting syntax error ; missing for this i am changing datatype as json now second error gone again i am getting cross domain how to solve both in my ajax call,why i am changing datatypes by Google suggestions….

  • Naman Sood says:

    Not working for me. I don’t no that, my hosting support `mod_headers.c` or not. Is there any other way to do so?

    Please, reply soon.

  • prince koli says:

    i am trying to post request from js through ajax call , to different domain (eg: payumoney) cross domain request . When i do this request it gives me

    error :XMLHttpRequest cannot load https://test.payumoney.com/payment/op/getPaymentResponse?merchantKey=8L4Jqz8p&merchantTransactionIds=%27d9da917455590f081890. Response to preflight request doesn’t pass access control check: The ‘Access-Control-Allow-Origin’ header contains multiple values ‘http://mediatest.payumoney.com, http://uxtest.payumoney.com, *’, but only one is allowed. Origin ‘http://localhost’ is therefore not allowed access.

    please kindly request me on this issue . what should i do?