Accéder à une API cross-domain depuis Javascript avec CORS et un reverse proxy nginx

Configurer un reverse proxy avec nginx et CORS pour permettre à une application Javascript d'accéder à une API sur un autre domaine en contournant la Same Origin Policy.

  1. Introduction
  2. CORS
  3. Reverse Proxy
  4. Bonus
  5. Conclusion

Introduction

Dans la continuité de l'émergence des applications full frontend, nous sommes de plus en plus amenés a appeler des API directement en Javascript depuis le client. J'ai récemment été confronté à un cas où l'API à interroger n'était pas sur le même domaine que l'application. Sur un développement backend ce genre de cas ne pose aucun problème mais avec Javascript, pour des raisons de sécurité, les communications cross-domain sont bloquées par la Same Origin Policy.

CORS

Cross-Origin Resource Sharing (CORS) est une spécification du W3C permettant les requêtes cross-domain depuis les navigateurs compatibles. Si l'API que vous interrogez est compatible avec CORS, vous pourrez accéder à l'API même si elle n'est pas sur le même domaine que votre application.

CORS est compatible avec :

  • Chrome 3+
  • Firefox 3.5+
  • Opera 12+
  • Safari 4+
  • Internet Explorer 8+

Pour utiliser CORS il faut envoyer au serveur des headers de contrôle d'accès qu'il inspectera pour approuver ou non la requête. Ces headers de contrôle d'accès décriront le contexte de la requête, sa méthode HTTP, son origine, ses headers custom, ...

Selon le type de requête, ces headers sont envoyés automatiquement par le navigateur avec la requête ou dans une requête préliminaire (preflight request). La requête aboutira si le serveur répond avec des headers de contrôle d'accès compatibles.

=> OPTIONS https://api.com/foobar
- HEADERS -
Origin: http://application.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Api-Key

<= HTTP/1.1 204 No Content
- RESPONSE HEADERS -
Access-Control-Allow-Origin: http://application.com
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Max-Age: 86400
Access-Control-Allow-Headers: Api-Key

Pour plus d'informations sur le fonctionnement de CORS, je vous laisse lire les articles Making Cross-Domain Requests with CORS et Démystifier CORS (Cross-Origin Resource Sharing) qui sont très complets.

Reverse Proxy

Malheureusement l'API que je souhaitais utiliser n'était pas compatible CORS. Si c'est votre cas également, il faudra alors passer par un proxy.

Nginx permet simplement de mettre en place ce genre de reverse proxy grace à une configuration de ce type :

server {
    listen          80;
    server_name     application.com;

    location /api {
        # Rewrite /api/resource/action?key=value to /resource/action?key=value
        RewriteRule                     ^/api/(.*)$ /$i [L,PT,QSA]

        # Activer le proxy
        proxy_set_header                X-Real-IP $remote_addr;
        proxy_set_header                X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass                      http://api.com;
        proxy_redirect                  off;
        proxy_buffers                   32 16k;
        proxy_busy_buffers_size         64k;
    }
}

Ainsi, votre application pourra appeler votre API sur http://application.com/api sans problème de cross-origin.

Si vous avez besoin d'exposer l'API sur un autre domaine ou sur un autre port que votre application, vous aurez besoin de permettre le cross-domain grâce à CORS. Mon application tournant sur localhost:8080, j'ai décidé de mettre mon proxy sur localhost:8181.

server {
    listen          8181;
    server_name     localhost;

    location / {

        # Activer le proxy
        proxy_set_header                X-Real-IP $remote_addr;
        proxy_set_header                X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass                      http://api.com;
        proxy_redirect                  off;
        proxy_buffers                   32 16k;
        proxy_busy_buffers_size         64k;

        # Ajouter les headers de contrôle d'accès CORS
        add_header    'Access-Control-Allow-Origin' '*' always;
        add_header    'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
        add_header    'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept' always;
        add_header    'Access-Control-Allow-Credentials' 'true' always;
}

Je peux ainsi appeler l'API sur localhost:8181 de façon transparente.

Attention, Access-Control-Allow-Origin: '*' autorise les requêtes cross-origin depuis n'importe quel domaine, en dev cela permet de facilement mettre CORS en place mais dans un soucis de sécurité il faudrait être plus restrictif.

Bonus

Vous pouvez encore améliorer votre proxy par exemple en ajoutant automatiquement des headers d'authentification si l'API en nécessite ou encore des headers de cache afin de reduire le temps de réponse ou d'économiser un éventuel quota imposé par l'API.

Conclusion

Grâce a nginx vous pouvez donc créer un reverse proxy qui vous permettra d'accéder à une API directement depuis votre application front sur un domaine différent en contournant de façon sécurisée la Same Origin Policy.