Partage de ressources d'origine croisée (CORS) avec nginx / chrome

13

J'ai un site Web avec la segmentation suivante:

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

Je voudrais autoriser les deux example.comet developers.example.comfaire des demandes AJAX à api.example.com.

Jusqu'à présent api.example.com, ma configuration nginx , qui est une application Rack desservie par unicorn, ressemble à ceci:

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

}

D'après ma lecture, cela devrait être suffisant pour ce que j'essaie de faire.

La réponse 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

Mais quand j'essaye ce qui suit dans la console 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);
})

Je vois:

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

Et de même pour http://example.com .

Qu'est-ce que je rate?

Si je mets le Access-Control-Allow-Originà *alors je vois:

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

Mais la demande jQuery échoue toujours, avec chrome soulignant également que le pré-vol a OPTIONSéchoué (même s'il est revenu 200 OK).

John Ledbetter
la source

Réponses:

17

Selon la spécification CORS, plusieurs origines doivent être séparées par des espaces, pas des virgules comme vous l'avez utilisé, alors essayez d'envoyer cet en-tête:

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

La documentation de Mozilla ne mentionne cependant pas plusieurs origines, donc si cela ne fonctionne toujours pas, essayez d'envoyer uniquement:

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

Si cela fonctionne, vous devrez configurer nginx ou votre serveur d'applications pour renvoyer un en- Access-Control-Allow-Origintête contenant la valeur de l'en- Origintête envoyé par le client s'il correspond à la liste autorisée. Quelque chose comme la configuration nginx suivante (non testée) pourrait le faire:

if ($http_origin ~ "^(http://developers.example.com|http://example.com)$") {
    add_header "Access-Control-Allow-Origin" $http_origin;
}
mgorven
la source
Cela fait partie de ce que j'ai fini par faire. J'ai également supprimé l'en- Access-Control-Allow-Headertête et modifié mon appel jQuery comme suit: $.ajax("http://api.example.com", { type: 'get', crossDomain: true}) ce qui a empêché le OPTIONScontrôle en amont de se produire.
John Ledbetter
1
REMARQUE: Si la solution donnée ne fonctionne pas pour vous, lisez ceci et ceci . C'est instructif, et vous pouvez trouver la raison pour laquelle cela ne fonctionne pas.
its_me
4

Utiliser un ifdans un locationbloc dans une configuration nginx comme ceci:

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

Faire nginx faire des choses étranges. Plus précisément, proxy_passet try_filesne fonctionnent pas comme prévu. Voir http://wiki.nginx.org/IfIsEvil pour plus d'informations.

Anthony Moralez
la source