Compartilhamento de Recursos de Origem Cruzada (CORS) com nginx / chrome

13

Eu tenho um site com a seguinte segmentação:

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

Eu gostaria de permitir ambos example.come developers.example.comfazer solicitações de AJAX para api.example.com.

Minha configuração do nginx até agora api.example.com, que é um aplicativo Rack sendo servido por unicórnio, fica assim:

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;
       }

}

Com base na minha leitura, isso deve ser suficiente para o que estou tentando fazer.

A resposta OPTIONS :

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

Mas quando tento o seguinte no console do Chrome:

$.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);
})

Eu vejo:

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

E o mesmo para http://example.com .

o que estou perdendo?

Se eu definir o Access-Control-Allow-Originpara, *então eu vejo:

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

Mas a solicitação do jQuery ainda falha, com o chrome também destacando que o pré-vôo OPTIONSfalhou (mesmo que tenha retornado 200 OK).

John Ledbetter
fonte

Respostas:

17

De acordo com a especificação do CORS, várias origens devem ser separadas por espaços, não vírgulas, como você usou, então tente enviar este cabeçalho:

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

A documentação do Mozilla não menciona várias origens, portanto, se isso ainda não funcionar, tente apenas enviar:

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

Se isso funcionar, você precisará configurar o nginx ou o servidor de aplicativos para retornar um Access-Control-Allow-Origincabeçalho contendo o valor do Origincabeçalho enviado pelo cliente, se ele corresponder à lista permitida. Algo como a seguinte configuração nginx (não testada) poderia fazer isso:

if ($http_origin ~ "^(http://developers.example.com|http://example.com)$") {
    add_header "Access-Control-Allow-Origin" $http_origin;
}
mgorven
fonte
Isso é parte do que acabei fazendo. Também removi o Access-Control-Allow-Headercabeçalho e modifiquei minha chamada do jQuery da seguinte maneira: O $.ajax("http://api.example.com", { type: 'get', crossDomain: true}) que impedia que o OPTIONSpreflight acontecesse.
John Ledbetter
1
NOTA: Se a solução fornecida não funcionar, leia isto e isto . É esclarecedor e você pode encontrar o motivo pelo qual não está funcionando.
its_me 28/02
4

Usando um ifem um locationbloco em uma configuração nginx como esta:

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

Faça com que o nginx faça coisas estranhas. Especificamente proxy_passe try_filesnão funciona como esperado. Veja http://wiki.nginx.org/IfIsEvil para mais informações.

Anthony Moralez
fonte