Cross-domain communication without CORS

In a recent project, I had to send data between two subdomains on a site. Unfortunately, this meant I had to deal with browsers' same origin policy. The canonical solution to this is to set up Cross-origin resource sharing (CORS) , but this is a bit of a pain to do1 - especially if you're only making one or two cross-domain requests - and it isn't supported by some older browsers. Luckily there are some alternatives.

JSONP

JSONP (JSON with padding) basically just means wrapping JSON in a function. So instead of this:

1
{ foo: 3, bar: 5 }

You have this:

1
callbackFunction({ foo: 3, bar: 5 })

Now all you have to do is define callbackFunction() and then load in your JSONP with a <script> tag:

1
2
3
4
5
6
7
<script src="http://otherdomain.com/jsonp.js"></script>
<script>
function callbackFunction(data) {
  console.log(data.foo);
  console.log(data.bar);
}
</script>

This is really easy to set up in Rails, you can just render :json as normal, but add a :callback option to turn it into JSONP:

1
2
3
4
5
6
7
def example
  respond_to do |format|
    format.js do 
      render json: { foo: 3, bar: 5 }, callback: "callbackFunction"
    end
  end
end

document.domain

This solution is even simpler, but only works if you're dealing with different subdomains (not different domains). You can tell pages from http://a.example.com and http://b.example.com to trust each other by setting their document.domain property (MDN) to point to the root domain:

1
2
// On both a.example.com and b.example.com
document.domain = "example.com"

However, if we tried to change document.domain to somewhereelse.com, we'd get an Illegal document.domain value error.

window.postMessage

If you have an iframe pulling content from a different domain, and you need the iframe and parent window to communicate, your best bet is window.postMessage (MDN). It's not compatible across all browsers, but its quite widely supported.

For example your iframe could send a message to the parent window like so:

1
window.parent.postMessage("hello", "*")

Which is handled by the parent window's message event handler:

1
2
3
window.addEventListener("message", function(e){
  if( e.data == "hello ") console.log("The iframe said hello!");
}, false);

You'd also probably want to validate the origin the message is being sent from, by checking e.origin.

Footnotes

  1. Alexey Vasiliev has a good tutorial for setting up CORS on Rails. Additionally, this Rack middleware may be of help: https://github.com/cyu/rack-cors.