Cross-domain AJAX using XMLHttpRequest

Author: Samuel Williams When: Friday, 06 May 2011

I recently implemented some cross-domain AJAX using jQuery. I wanted to POST data using JavaScript's XMLHttpRequest to another site, and this required the use of the new HTTP access control headers[1].

You can try out the functionality on the new site - simply click the "Contact Us" link in the footer and send us message. That form POSTs data to which is responsible for actually sending us a message.

To make this work, you need to respond to the HTTP OPTIONS method with appropriate access control headers. When the client web browser tries to send the XMLHttpRequest, it first initiates an OPTIONS request to the same URI, and checks the headers. These headers specify things such as what domains can make XMLHttpRequests to this URI.

Server-side implementation

Here is an example of how to respond to the HTTP options method using the Utopia framework:

	"Access-Control-Allow-Origin" => "*",
	"Access-Control-Allow-Headers" => "X-Requested-With",
	"Access-Control-Max-Age" => "60"

on 'index' do |request, path|
	# ...
	if request.options?
		# Allow AJAX requests from different domains.
		return :status => 200, :headers => ACCESS_CONTROLS
	# ...

You can check this using curl -i -X OPTIONS $URI:

$ curl -i -X OPTIONS 
HTTP/1.1 200 OK
Date: Fri, 06 May 2011 02:25:28 GMT
Server: Apache/2.2.16 (Debian)
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.5
Access-Control-Allow-Headers: X-Requested-With
Access-Control-Allow-Origin: *
Access-Control-Max-Age: 60
Content-Length: 0
Status: 200
Content-Type: text/plain

One problem I encountered when executing XMLHttpRequests was the fact that they process 3xx redirections transparently. So, if you return a 3xx redirect, it won't actually return this status code to your handler, but instead process the redirection[3]. For cross-domain requests, this can be a big problem unless you correctly specify OPTIONS for the redirected page too. Therefore, it is wise to ensure that controllers that process XMLHttpRequests return 2xx or 4xx status codes.

It is also important that controllers return the access control headers.

if request.xhr?
	# You also need to provide access control headers here.
	return :status => :success, :headers => ACCESS_CONTROLS
	return redirect(params["from"] ? "success" : "success-no-reply")

Client-side implementation

On the, I use JavaScript and jQuery to serialize the contact form and send this data to

$(function() {
	// The contact form element
	var contact = $("#contact");

		rules: {
			subject: "required",
			from: {
				email: true
			message: "required"
		submitHandler: function() {
			$('#contact-popup .main').slideUp();
			$('#contact-popup .sending').slideDown();
				url: contact.attr('action'),
				type: 'post',
				data: contact.serialize(),
				crossDomain: true,
				headers: {
					"X-Requested-With": "XMLHttpRequest"
				success: function() {
					$('#contact-popup .sending').slideUp();
					$('#contact-popup .success').slideDown();
				error: function(request, status, error) {
					$('#contact-popup .sending').slideUp();
					$('#contact-popup .error').slideDown();

jQuery by default doesn't set X-Requested-With for cross-domain requests, so you need to do this manually.

Further Reading

  1. Mozilla Developer: HTTP Access Controls
  2. WebKit DOM Programming Topics: Security Considerations
  3. XMLHttpRequest: Infrastructure for the send method
  4. Secure login using AJAX
  5. Rack Memory Usage


whats the browsers coverage on this?

@wookiehangover All recent versions of Firefox, Safari and Chrome support this. IE supports this via XDomainRequest which isn’t supported by jQuery yet (probably because its proprietary).

Interesting article.

If anybody is looking to implement and consume a cross-domain JSON API with Rails and jQuery (using JSONP), I’ve written about it here: Consuming a public Rails API with jQuery.

Like David said, I think what you are looking for is jsonp through jQuery. I did something similar to David in Grails using jsonp having jQuery consume rest service data. The only difference in my code was adding a check for “html” type requests instead of json or xml so that it could append the callback param onto the beginning. There are several examples out there. You can check out one such example here.

If you have no control over the end point code and are consuming someone else’s data, then you might want to consider doing ajax locally and having your server invoke the endpoint to consume, format and return the data back to the client.

[City], [Country]
Publicly displayed.
Your email won't be displayed.
The following tags are preserved: <pre>, <em> and <a>. All comments are moderated.

Please note, you can leave a comment that uses (limited) XHTML and Textile syntax.