Serveurs Vps Nginx Reverse proxy

NGINX reverse proxy : exposer plusieurs services

NGINX reverse proxy pour plusieurs services : chemins proxy_pass, en-têtes fiables, WebSocket, frontière TLS et diagnostic avant rechargement.

Julien Bernard
Julien Bernard

Serveurs dédiés, infogérance et migration

Il couvre les serveurs dédiés, l'infogérance, le stockage, la supervision et les plans de sortie.

10 min de lecture

Un reverse proxy NGINX reçoit les requêtes HTTPS, choisit le service cible selon le nom d’hôte ou le chemin, puis renvoie sa réponse au client. Une configuration fiable doit préserver les URI, transmettre seulement les en-têtes utiles, traiter WebSocket explicitement et définir clairement où le TLS se termine.

Le bloc minimal trouvé dans un exemple fonctionne rarement comme contrat universel. Une barre oblique change le chemin envoyé. Un en-tête transmis sans frontière de confiance peut fausser l’adresse cliente. Un tunnel WebSocket exige un changement de protocole explicite. Et un cadenas côté navigateur ne dit rien du segment entre NGINX et l’application.

Ce guide part de deux services HTTP déjà fonctionnels sur la machine. Il montre des choix, pas un fichier à coller sans lecture. Testez chaque application directement, identifiez le chemin qu’elle attend et sachez qui peut joindre son port avant d’ajouter NGINX.

Le proxy inverse est une frontière, pas un simple relais

NGINX devient le point d’entrée public. Il reçoit la connexion, sélectionne un bloc server selon le nom demandé, choisit ensuite une location selon l’URI et transmet la requête à l’upstream configuré. Chaque étape peut modifier le comportement ; il faut donc écrire son intention avant les directives.

Relevez ces éléments pour chaque service :

  • nom public : domaine ou sous-domaine présenté au navigateur ;
  • adresse interne : socket, adresse loopback ou nom résolu sur un réseau privé ;
  • racine attendue : l’application sert-elle sous / ou sous un préfixe ;
  • protocole : HTTP classique, WebSocket, flux long ou téléchargement volumineux ;
  • identité cliente : quels en-têtes l’application sait lire et quelles sources elle doit croire ;
  • frontière TLS : terminaison dans NGINX ou chiffrement maintenu jusqu’à l’upstream ;
  • signal de panne : journal, page de santé et procédure de retour arrière.

Un proxy inverse concentre aussi le risque. Une erreur de routage touche plusieurs applications ; une clé privée mal protégée touche tous les noms qu’elle couvre. Avant de mutualiser l’entrée, écrivez la panne la plus plausible et son chemin de retour.

Choisir le routage par domaine ou par chemin

Deux modèles dominent. Le routage par nom donne un sous-domaine à chaque application. Le routage par chemin place plusieurs applications derrière le même domaine. Le premier respecte généralement mieux les URL natives ; le second exige de connaître précisément les préfixes, redirections et chemins absolus produits par chaque logiciel.

ModèleExemple publicAvantagePiège principal
Nom d’hôtesite.example.frURI inchangée, séparation netteDNS et certificat pour chaque nom
Nom d’hôteapi.example.frPolitique distincte par serviceOrigines et cookies à vérifier
Préfixeexample.fr/app/Un seul domaine visibleRéécriture des chemins et des redirections
Préfixeexample.fr/api/Frontière facile à annoncerApplication parfois incapable de vivre sous un préfixe

Préférez le nom d’hôte lorsque l’application suppose être à la racine. Choisissez un préfixe seulement si elle accepte un chemin de base configurable ou si vous avez vérifié ses URL, cookies, redirections et ressources statiques. NGINX peut réécrire une URI ; il ne répare pas une application qui fabrique partout des liens absolus incohérents.

Construire une base par noms d’hôte

L’extrait suivant appartient au contexte http de NGINX. Les upstreams écoutent uniquement sur l’adresse loopback de l’hôte. Le certificat présenté doit couvrir les deux noms, ou chaque bloc doit recevoir son propre certificat. Les chemins sont des exemples : adaptez-les à votre installation et à votre mécanisme de renouvellement.

upstream service_web {
    server 127.0.0.1:9000;
}

upstream service_api {
    server 127.0.0.1:9100;
}

server {
    listen 80;
    server_name site.example.fr api.example.fr;
    return 308 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name site.example.fr;

    ssl_certificate     /etc/nginx/tls/example-fullchain.pem;
    ssl_certificate_key /etc/nginx/tls/example-privkey.pem;

    location / {
        proxy_pass http://service_web;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 443 ssl;
    server_name api.example.fr;

    ssl_certificate     /etc/nginx/tls/example-fullchain.pem;
    ssl_certificate_key /etc/nginx/tls/example-privkey.pem;

    location / {
        proxy_pass http://service_api;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Les ports applicatifs ne doivent pas être publics si seul NGINX les utilise. Sur un VPS, liez-les à loopback ou au réseau privé approprié, puis contrôlez l’écoute réelle avec les outils du système. Le pare-feu complète cette limite ; il ne corrige pas une application liée par erreur à toutes les interfaces.

La clé privée mérite son propre contrôle. NGINX doit pouvoir la lire au démarrage, mais elle ne doit pas devenir lisible par tous les comptes de la machine. Vérifiez aussi que le fichier de certificat contient la chaîne attendue. Un fichier présent n’est pas nécessairement une chaîne correcte.

Comprendre la barre oblique de proxy_pass

La forme de proxy_pass décide si NGINX conserve ou remplace la partie de l’URI qui correspond à location. Cette différence explique une grande part des erreurs où l’upstream reçoit un chemin doublé, amputé ou inattendu.

location /app/ {
    proxy_pass http://service_web;
}

Avec cette forme sans URI ajoutée à proxy_pass, la requête /app/doc est transmise avec ce chemin complet. L’application doit donc connaître le préfixe /app/.

location /app/ {
    proxy_pass http://service_web/;
}

Ici, la barre oblique constitue une URI. NGINX remplace le préfixe correspondant à location : la même requête arrive à l’upstream sous /doc. Ce comportement est utile quand le public voit /app/ mais que l’application sert à la racine.

locationproxy_passURI publiqueURI envoyée
/app/Upstream sans URI/app/doc/app/doc
/app/Upstream avec //app/doc/doc

Écrivez un test avec une URI réelle avant le rechargement. Les locations régulières, les réécritures et les variables ajoutent d’autres règles ; n’étendez pas ce tableau à ces cas sans relire la documentation de la directive.

Transmettre les en-têtes sans inventer la confiance

NGINX redéfinit certains en-têtes lorsqu’il transmet une requête. Les quatre lignes de l’exemple rendent le contrat explicite : nom public, adresse du pair direct, chaîne d’adresses et protocole reçu par NGINX.

X-Forwarded-For n’est pas une preuve d’identité. La variable qui ajoute l’adresse cliente conserve aussi une éventuelle chaîne reçue. L’application doit faire confiance à NGINX, pas à n’importe quel client capable d’envoyer son propre en-tête. Si un CDN ou un autre proxy précède NGINX, définissez précisément quelles adresses intermédiaires sont fiables.

Le protocole transmis influence souvent les redirections et les cookies sécurisés. Si l’application croit recevoir HTTP alors que le navigateur utilisait HTTPS, elle peut produire une boucle de redirection ou une URL incorrecte. L’application doit toutefois être configurée pour croire X-Forwarded-Proto uniquement lorsqu’il vient de votre proxy.

Le choix de Host dépend du besoin de l’application. La variable $host garde le nom public sélectionné par NGINX. Laisser la valeur par défaut expose plutôt le nom de l’upstream. Ne recopiez pas un en-tête par habitude : documentez le consommateur et la conséquence.

Traiter WebSocket comme un changement de protocole

WebSocket commence par HTTP puis demande un changement de protocole. Les en-têtes Upgrade et Connection sont hop-by-hop ; NGINX ne les transmet pas automatiquement. Le modèle officiel utilise map pour envoyer upgrade seulement lorsqu’un client a réellement demandé ce changement.

Placez map dans le contexte http, puis limitez les en-têtes au chemin WebSocket :

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 443 ssl;
    server_name api.example.fr;

    location /socket/ {
        proxy_pass http://service_api;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Cette forme conserve le préfixe /socket/ parce que proxy_pass ne contient pas d’URI. Si l’upstream attend la racine, ajoutez une URI et vérifiez le chemin obtenu. Le timeout dépend de l’application : préférez des messages de maintien en vie utiles et une valeur justifiée plutôt qu’un délai énorme copié au hasard.

Dessiner la frontière TLS

Terminer TLS dans NGINX signifie que le navigateur chiffre jusqu’au proxy. Le segment suivant est une décision séparée. Un upstream en loopback sur un hôte maîtrisé peut utiliser HTTP selon votre modèle de menace. Un service distant, un réseau partagé ou une exigence de chiffrement de bout en bout justifie HTTPS avec vérification du certificat upstream.

La frontière TLS se trouve dans NGINX : le segment suivant dépend du niveau de confiance du réseau interne, pas d’une règle universelle.

Schéma montrant un client HTTPS, la terminaison TLS dans NGINX et deux services sur le réseau interne
Dans cet exemple, NGINX termine le TLS public puis distribue la requête vers le service web ou l’API. Un réseau interne non fiable demande aussi un upstream HTTPS vérifié.

Pour un upstream HTTPS signé par une autorité interne, configurez la confiance et activez la vérification. Mettre seulement https dans proxy_pass chiffre la liaison, mais ne définit pas à lui seul quelle identité doit être acceptée.

location / {
    proxy_pass https://service_api_tls;
    proxy_ssl_server_name on;
    proxy_ssl_name api.internal.example;
    proxy_ssl_trusted_certificate /etc/nginx/ca/internal-ca.pem;
    proxy_ssl_verify on;
}

Le nom vérifié, la chaîne de confiance et la résolution de l’upstream doivent correspondre. Si vous ne contrôlez pas ces trois éléments, vous avez déplacé le problème derrière le proxy.

Valider et diagnostiquer par couches

Ne rechargez jamais avant le test de configuration. La commande de test vérifie la syntaxe et tente d’ouvrir les fichiers référencés, notamment les certificats. La variante qui affiche toute la configuration résolue aide à repérer une directive héritée ou un fichier inclus inattendu ; protégez sa sortie si elle contient des informations sensibles.

nginx -t
nginx -T

Ensuite, testez de l’intérieur vers l’extérieur :

curl -v http://127.0.0.1:9000/
curl -v http://127.0.0.1:9100/
curl --resolve site.example.fr:443:127.0.0.1 https://site.example.fr/
curl --resolve api.example.fr:443:127.0.0.1 https://api.example.fr/

Le certificat doit couvrir le nom utilisé par curl. Ne désactivez pas sa vérification pour faire disparaître une erreur : vous masqueriez précisément la frontière que vous cherchez à valider.

SymptômeCouche probableVérification suivante
Refus de connexion upstreamProcessus ou adresse interneÉcoute réelle et journal applicatif
Réponse 502Connexion ou réponse upstream invalideerror_log et test direct de l’upstream
Réponse 504Upstream silencieux jusqu’au délaiTemps de connexion et de réponse
Boucle de redirectionHost ou protocole public mal interprétéEn-têtes reçus par l’application
WebSocket fermé au démarrageUpgrade ou Connection absentmap, location et journal upstream
Mauvais cheminContrat proxy_pass incohérentURI publique contre URI reçue

Les journaux d’accès disent quelle requête est sortie de NGINX. Le journal d’erreur explique souvent pourquoi la connexion upstream a échoué. Ajoutez temporairement les temps de connexion et de réponse upstream au format d’accès pour distinguer une attente réseau d’une application lente.

Après un test vert, rechargez proprement puis répétez les requêtes externes. Gardez la configuration précédente disponible pour un retour rapide. Le retour arrière appartient au coût d’exploitation, même lorsqu’il ne concerne que deux noms de domaine.

Quand choisir ou éviter cette architecture

SituationReverse proxy NGINXRéserve à traiter
Plusieurs services sur un hôteAdaptéPorts internes, journaux et sauvegarde de configuration
Plusieurs domaines sur une adresse publiqueAdaptéDNS, certificats et isolation des virtual hosts
Application compatible avec un préfixePossibleURI, cookies, redirections et ressources statiques
Réseau upstream non fiablePossibleHTTPS upstream avec identité vérifiée
Découverte dynamique complexe de servicesLimité seulOutil d’orchestration ou registre adapté
Haute disponibilité de la passerelle exigéeInsuffisant seulDeuxième entrée, état, DNS ou bascule supervisée

Liste de contrôle

  • Vérifiez chaque upstream directement et bloquez tout port applicatif exposé au-delà du réseau prévu.
  • Testez une URI réelle par location afin de confirmer le chemin exact produit par proxy_pass.
  • Définissez les proxies de confiance avant d’utiliser les en-têtes forwarded pour l’adresse cliente ou le protocole public.
  • Validez syntaxe, certificats, WebSocket et retour arrière avant chaque rechargement de la passerelle.

Questions fréquentes

Faut-il une barre oblique après l’upstream dans proxy_pass ?
Cela dépend du chemin attendu. Sans URI, NGINX conserve le chemin complet ; avec une URI telle que /, il remplace le préfixe correspondant à location.
Dois-je transmettre X-Forwarded-For ?
Oui si l’application en a besoin, mais elle doit croire uniquement votre proxy et connaître la chaîne de proxies autorisés. Cet en-tête ne prouve pas seul l’identité du client.
Pourquoi WebSocket échoue-t-il alors que HTTP fonctionne ?
Les en-têtes Upgrade et Connection ne traversent pas automatiquement le proxy. Transmettez-les explicitement avec un map et vérifiez aussi l’URI attendue par l’upstream.
Le trafic entre NGINX et l’application doit-il utiliser HTTPS ?
Le choix dépend de la confiance dans le segment interne. Hors d’un hôte ou réseau strictement maîtrisé, utilisez HTTPS upstream avec une autorité et un nom vérifiés.

Préparé par

Julien Bernard
Julien Bernard

Serveurs dédiés, infogérance et migration

Il couvre les serveurs dédiés, l'infogérance, le stockage, la supervision et les plans de sortie.

Faits vérifiés

HostScout editorial

Articles liés