Using Access-Control-Allow-Origin to make cross domain POST requests from javsacript

Making ajax calls from javascript, even without a framework like jQuery, is pretty trivial. However, once you try to make the same request cross-domain, it gets hard fast. This is due to the security model all modern browsers use, known as the same origin policy. This policy makes sense in a lot of ways, but it's also somewhat broken and antiquated on the web today.

Why might you want to make cross domain requests? Just take a look at some examples:

  • Google Analytics sends metrics from your page to their central servers.
  • HousingMaps, a mashup of craigslist and GoogleMaps.
  • The Read Later Instapaper bookmarklet.

For cross domain ajax requests, the same origin policy is "broken" in the sense that there have been work-arounds available for a years. First there were proxy pages, where you wrap a remote URL in a page being served by the site the user is actually on. The proxy requires that you be in control of the page the user is on. Then there was a dual IFRAME hack, but that requires that you be in control of both ends.

Recently, JSONP has emerged as a standard to allow cross domain requests. It's something of a hack that works by returning an executable javascript function from a script tag request. It also exposes you to potential javascript injection vulnerabilities if you are not in control of the remote host. On the plus side, it's provided seamlessly as a feature in recent jQuery versions.

Luckily, there is a true standard emerging that's not built on top of a hack. In 2004, the W3C started work on a draft called Cross-Origin Resource Sharing (CORS). In 2009, Mozilla was the first browser to implement support for CORS. Currently Chrome also supports it. It allows a HTTP server to set some new headers that tell the browser to modify its same origin policy.

For example, the following Django code with allow an ajax request from another domain to your server.

def myview(_request):
    response = HttpResponse(json.dumps({"key": "value", "key2": "value"}))
    response["Access-Control-Allow-Origin"] = "*"
    response["Access-Control-Allow-Methods"] = "POST, GET, OPTIONS"
    response["Access-Control-Max-Age"] = "1000"
    response["Access-Control-Allow-Headers"] = "*"
    return response

In typical fashion, Microsoft went ahead and implemented their own incompatible version of the same thing, which they call XDR. If you're masochistic, you can try to use it. Otherwise, you can wait until IE10 when they will support CORS, too.



I'm currently working at NerdWallet, a startup in San Francisco trying to bring clarity to all of life's financial decisions. We're hiring like crazy. Hit me up on Twitter, I would love to talk.

Follow @chase_seibert on Twitter