Intercambio de recursos de origen cruzado (CORS) con nginx / chrome

13

Tengo un sitio web con la siguiente segmentación:

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

Me gustaría permitir ambos example.comy developers.example.comhacer solicitudes AJAX api.example.com.

Mi configuración nginx hasta ahora api.example.com, que es una aplicación de Rack servida por unicornio, se ve así:

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

}

Según mi lectura, esto debería ser suficiente para lo que estoy tratando de hacer.

La respuesta de OPCIONES :

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

Pero cuando intento lo siguiente en la consola de 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);
})

Veo:

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

Y lo mismo para http://example.com .

¿Qué me estoy perdiendo?

Si configuro el Access-Control-Allow-Origina *entonces veo:

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

Pero la solicitud de jQuery todavía falla, con Chrome también destacando que el pre-vuelo OPTIONSfalló (a pesar de que regresó 200 OK).

John Ledbetter
fuente

Respuestas:

17

De acuerdo con la especificación CORS, los orígenes múltiples deben estar separados por espacios, no por comas, como ha utilizado, así que intente enviar este encabezado:

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

Sin embargo, la documentación de Mozilla no menciona orígenes múltiples, por lo que si eso todavía no funciona, intente solo enviar:

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

Si eso funciona, deberá configurar nginx o su servidor de aplicaciones para que devuelva un Access-Control-Allow-Originencabezado que contenga el valor del Originencabezado enviado por el cliente si coincide con la lista permitida. Algo así como la siguiente configuración nginx (no probada) podría hacer eso:

if ($http_origin ~ "^(http://developers.example.com|http://example.com)$") {
    add_header "Access-Control-Allow-Origin" $http_origin;
}
mgorven
fuente
Esto es parte de lo que terminé haciendo. También eliminé el Access-Control-Allow-Headerencabezado y modifiqué mi llamada a jQuery de esta manera: lo $.ajax("http://api.example.com", { type: 'get', crossDomain: true}) que evitó que OPTIONSocurriera la verificación previa.
John Ledbetter
1
NOTA: Si la solución dada no funciona para usted, lea esto y esto . Es esclarecedor y puede encontrar la razón por la que no funciona.
its_me
4

Usando un ifen un locationbloque en una configuración nginx como esta:

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

Hacer que nginx haga cosas raras. Específicamente, proxy_passy try_filesno funciona como se esperaba. Ver http://wiki.nginx.org/IfIsEvil para más información.

Anthony Moralez
fuente