Nginx – Cross Origin Resource Sharing (CORS) with nginx / chrome

nginx

I have a website with the following segmentation:

api.example.com 
developers.example.com 
example.com

I would like to allow both example.com and developers.example.com to make AJAX requests to api.example.com.

My nginx configuration so far for api.example.com, which is a Rack app being served by unicorn, looks like this:

upstream app_server {
  server unix:/tmp/api.example.com.sock fail_timeout=0;
}

server {
       listen 80;
       server_name api.example.com;
       access_log /home/nginx/api.example.com/log/access.log;
       error_log /home/nginx/api.example.com/log/error.log;
       location / {
         add_header 'Access-Control-Allow-Origin' 'http://example.com,http://developers.example.com';
         add_header 'Access-Control-Allow-Credentials' 'true';
         add_header 'Access-Control-Allow-Headers' 'Content-Type,Accept';
         add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';

         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header Host $http_host;
         proxy_redirect off;
         proxy_pass http://app_server;
       }

}

Based on my reading, this should be sufficient for what I'm trying to do.

The OPTIONS response:

HTTP/1.1 200 OK
Server: nginx/0.7.67
Date: Sat, 28 Apr 2012 17:20:08 GMT
Content-Type: application/json
Connection: close
Status: 200 OK
Content-Length: 0
Access-Control-Allow-Origin: http://developers.example.com,http://example.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type,Accept
Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE

But when I try the following in the Chrome console:

$.ajax("http://api.example.com", {
  type: 'get',
  contentType: "application/json",
  accept: "application/json"
}).success(function(data){
  console.log("success!", data);
}).fail(function(jqxhr, statusText){
  console.log("fail!", jqxhr, statusText);
})

I see:

XMLHttpRequest cannot load http://api.example.com/. Origin
http://developers.example.com is not allowed by Access-Control-Allow-Origin.

And the same for http://example.com.

What am I missing?

If I set the Access-Control-Allow-Origin to * then I see:

HTTP/1.1 200 OK
Server: nginx/0.7.67
Date: Sat, 28 Apr 2012 17:28:41 GMT
Content-Type: application/json
Connection: close
Status: 200 OK
Content-Length: 0
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type,Accept
Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE

But the jQuery request still fails, with chrome also highlighting that the pre-flight OPTIONS failed (even though it returned 200 OK).

Best Answer

According to the CORS spec multiple origins should be separated by spaces, not commas as you've used, so try sending this header:

Access-Control-Allow-Origin: http://developers.example.com http://example.com

The Mozilla documentation doesn't mention multiple origins though, so if that still doesn't work try only sending:

Access-Control-Allow-Origin: http://developers.example.com

If that works you'll need to configure nginx or your application server to return an Access-Control-Allow-Origin header containing the value of the Origin header sent by the client if it matches the allowed list. Something like the following (untested) nginx configuration could do that:

if ($http_origin ~ "^(http://developers.example.com|http://example.com)$") {
    add_header "Access-Control-Allow-Origin" $http_origin;
}