Enabling Cross Domain Ajax Requests using CORS
Okay, I usually harped about the one main limitation of Ajax Requests – Same Origin Policy. This policy exists to restrict XSS (Cross Site Scripting) attacks and vulnerabilities. But that is also a big party pooper when you want to legally make an Ajax call from your site to another domain, directly from the browser in order to load content from the other domain. There are exceptions to this policy that allow for embedding of certain resources like images, scripts, stylesheets, media and some other types of content. Thus CDNs for loading scripts like jQuery and stylesheets from Bootstrap are quite common and actually recommended too. But that’s not Ajax. Ajax begins when an XMLHttpRequest object is created. And that object cannot normally be used to make a request to any other domain other than the one it is currently on.
For example, if a script on a page on the domain http://DomainA.com wants make an Ajax request, it is limited to make the request to any resource only on the http://DomainA.com domain by the Same Origin Policy. If the script tries to make a request to http://DomainB.com, it will be actively blocked by the browser. The policy applies to Port numbers, sub-domains and protocols too. This is a good thing to protect users from visiting untrusted sites and having those sites steal the users sessions from trusted websites. But to legally make a script load content from another domain, say like your recent tweets, your facebook posts or your flickr feed or weather data or stock info from an online API or Web Service, this policy restricts you to adopting one of the following techniques to achieve your goal:
- Use a Server Side Proxy
- Use JSONP (if the remote API supports it)
I will leave the JSONP technique for another day and get to my subject now. The W3C made CORS (Cross Origin Resource Sharing) a Recommendation in January 2014, a document that was in the works for some time now.
The CORS Recommendation allows a way around for web servers to allow for cross domain script requests. Servers can now do this by adding new headers in the HTTP response to a cross domain request, namely:
1 |
Access-Control-Allow-Origin: <domain> | * |
By sending this new header, the server can let the browser making the request know that it can allow the request and response to be used by the script, and hence not block the request. You can read more details about the CORS spec on W3.org and MDN. This is great for API developers who want to make a service available for consumption from simple Ajax requests from any domain.
I will jump to my demo now. http://sreenath.net/demos/CORSDemo.htm
To demonstrate this, I use the OpenWeatherMap’s REST API to provide me the cross domain content. And I use a standard XMLHttpRequest object pointed to the Open Weather Map API’s URL for retrieving the current weather data for the requested city.
For example, if I want to get the current weather conditions of New York, NY, my XHR’s URL will look like this:
1 |
http://api.openweathermap.org/data/2.5/weather?q=New%20York,%20NY |
and my request headers (on chrome) look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
GET /data/2.5/weather?q=New%20York,%20NY HTTP/1.1 Host: api.openweathermap.org Connection: keep-alive User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36 Origin: http://sreenath.net Accept: */* DNT: 1 Referer: http://sreenath.net/demos/CORSDemo.htm Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8,kn;q=0.6 |
And the response headers from the server look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
HTTP/1.1 200 OK Server: nginx Date: Mon, 28 Apr 2014 02:03:28 GMT Content-Type: application/json; charset=utf-8 Transfer-Encoding: chunked Connection: keep-alive Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true Access-Control-Allow-Methods: GET, POST |
Yep, a 200 OK response alright. And since I didn’t specify a format, I would receive the default JSON format from the server containing the current weather conditions.
Observe the last three response headers. They are all from the CORS spec and there are more. To go deeper into this topic, Monsur Hossain has published a book “CORS In Action“, available Sept 2014 in print or now through MEAP. You can also visit Enable CORS website to get updated information and use this link to know about browser support for this new spec.
With ASP.NET Web API where it currently is, this development greatly enhances the idea of Service Oriented Applications and also emphasizes the role of JavaScript in the world of programming today. Get cracking!
Questions? Comment below ↓
Hi, it is possible that openweathermap.org disable CORS?
Your CORSDemo.htm don’t work an the Chrome console log:
“XMLHttpRequest cannot load http://api.openweathermap.org/data/2.5/weather?q=London. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://sreenath.net’ is therefore not allowed access.”
Hi Hambert
Only saw your comment now. Thanks for reading through my post. I checked my demo and it is working even now and I see that OpenWeatherMap is still sending the Access-Control-Origin header as expected in the Ajax response.
I would like to know more about the conditions that you had that made you get that error.
Sreenath
It’s not working for me either. Same error 🙁
Hi Tiago
I confirm that it was not working and it was actually because OpenWeatherMap now requires users to register and provide an AppID with each request which was not the case at the time of the writing of the blog.
I’ve updated the demo now to include a free AppId in the request and it works as before. Do check it out now. Do a Cntrl+Refresh if it doesn’t seem to work just in case the old file was cached in your browser. Thanks for bringing it to my attention. And thanks for reading through my blog post.
Sreenath
Great !!
Nice article, thanks. If you need to make more advanced CORS requests (preflight, credentials, supports IE) see: https://zinoui.com/blog/cross-domain-ajax-request