Cross-domain AJAX, Express.js and Access-Control-Allow-Origin

I have a tip for anyone working with node.js frameworks, in my case it's the Express framework, but this applies to any framework that binds to URL patterns.

I've been working out some JavaScript on jsFiddle that involves AJAX requests to an endpoint I have running on a node.js server external to jsFiddle. Part of this requires that the Access-Control-Allow-Origin header be setup to allow access from external domains. You can do this in Express by adding the header to your response object with:

res.header("Access-Control-Allow-Origin", "*");

I had a route that looked something like this:

app.get('/posts', function(req, res){
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.send(
{ posts : ... }
);
});


But it wasn't working in Chrome, I kept seeing the error "XMLHttpRequest cannot load http://... Origin http://fiddle.jshell.net is not allowed by Access-Control-Allow-Origin." even though I was setting the correct header.

Turns out that there was a OPTIONS request being sent before the GET request that didn't match my route and it was failing with the previous error. Problem is, Chrome didn't show the request in the Inspector's Network tab so it took me a long time to figure out what was happening.

The fix is a pretty simple one; you need to make sure your route in Express matches the OPTIONS request as well as the GET request. The easiest way to do that is to bind with app.all(...) to match on all requests. So the updated and working code is:

app.all('/posts', function(req, res){
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.send(
{ posts : ... }
);
});


It's also important to note that I needed to set the Access-Control-Allow-Headers header to allow "X-Requested-With". Without it, it generated a similar error: "XMLHttpRequest cannot load http://... Request header field X-Requested-With is not allowed by Access-Control-Allow-Headers."