Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

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).

share|improve this question
add comment

1 Answer

up vote 10 down vote accepted

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;
}
share|improve this answer
    
This is part of what I ended up doing. I also removed the Access-Control-Allow-Header header, and modified my jQuery call like so: $.ajax("http://api.example.com", { type: 'get', crossDomain: true}) Which prevented the OPTIONS preflight from happening at all. –  John Ledbetter May 1 '12 at 13:05
    
NOTE: If the given solution doesn't work for you, read this and this. It's enlightening, and you may find the reason it isn't working. –  its_me Feb 28 '13 at 8:22
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.