Configurer Nginx comme reverse proxy

12 min de lecture·Matthieu|

Configurez Nginx comme reverse proxy pour Node.js, Ollama et d'autres backends. Ce guide couvre proxy_pass, le transfert de headers, le support WebSocket, le load balancing upstream et le réglage des timeouts en production, avec vérification à chaque étape.

Un reverse proxy se place entre les clients et votre application backend. Nginx reçoit les requêtes entrantes, les transmet à un serveur backend (Node.js, Python, Go, Ollama) et renvoie la réponse au client. Cela permet d'ajouter la terminaison TLS, le load balancing, le cache et le contrôle d'accès sans modifier votre application.

Ce tutoriel couvre la configuration reverse proxy de Nginx, d'un simple proxy_pass au proxy WebSocket, au load balancing upstream et à une configuration Ollama prête pour la production.

Prérequis

Avant de commencer, vous avez besoin de :

Vérifiez que Nginx tourne :

sudo systemctl status nginx

Vous devriez voir active (running) dans la sortie.

Comment configurer proxy_pass dans Nginx ?

La directive proxy_pass indique à Nginx où transmettre les requêtes. Elle se place dans un bloc location à l'intérieur d'un bloc server. Nginx envoie la requête du client à l'URL backend spécifiée et retransmet la réponse. La directive accepte les URL HTTP et HTTPS, et peut pointer vers une adresse IP, un nom de domaine ou un socket Unix.

Créez un nouveau fichier de bloc serveur :

sudo nano /etc/nginx/sites-available/app.conf

Ajoutez une configuration reverse proxy minimale :

server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Activez le site et testez la configuration :

sudo ln -s /etc/nginx/sites-available/app.conf /etc/nginx/sites-enabled/
sudo nginx -t

Si nginx -t renvoie syntax is ok et test is successful, rechargez :

sudo systemctl reload nginx

Vérifiez que le proxy fonctionne :

curl -I http://app.example.com

Les headers de réponse devraient provenir de votre application backend. Si vous voyez un 502 Bad Gateway, votre backend ne tourne pas sur le port 3000.

Que se passe-t-il avec un slash final dans proxy_pass ?

Le slash final dans proxy_pass contrôle la réécriture de l'URI. C'est l'une des sources de mauvaise configuration les plus fréquentes.

Configuration Requête Transmis au backend
proxy_pass http://127.0.0.1:3000 /app/users http://127.0.0.1:3000/app/users
proxy_pass http://127.0.0.1:3000/ /app/users http://127.0.0.1:3000/users
proxy_pass http://127.0.0.1:3000/v2/ /app/users http://127.0.0.1:3000/v2/users
proxy_pass http://127.0.0.1:3000/v2 /app/users http://127.0.0.1:3000/v2users

La règle : quand proxy_pass inclut un URI (tout ce qui suit host:port, même juste /), Nginx supprime le préfixe location correspondant de l'URI de la requête et ajoute le reste à l'URI de proxy_pass. Quand proxy_pass n'a pas d'URI, le chemin original passe tel quel.

Pour un bloc location /app/, les exemples ci-dessus s'appliquent. Notez la quatrième ligne : sans slash final sur /v2, le chemin devient /v2users au lieu de /v2/users. Ajoutez toujours un slash final quand vous spécifiez un chemin.

Quels headers transmettre au backend ?

Sans configuration explicite des headers, votre application backend ne peut pas voir l'adresse IP réelle du client, le protocole original ou le nom d'hôte demandé. Nginx les remplace par défaut. Transmettez-les manuellement.

Header Valeur Rôle
Host $host Conserve le header Host original pour que le backend sache quel domaine a été demandé
X-Real-IP $remote_addr Transmet l'adresse IP du client au backend
X-Forwarded-For $proxy_add_x_forwarded_for Ajoute l'IP du client à la chaîne de proxies
X-Forwarded-Proto $scheme Indique au backend si la requête originale utilisait HTTP ou HTTPS

Ajoutez ces headers à votre bloc location :

location / {
    proxy_pass http://127.0.0.1:3000;
    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;
}

Après rechargement de Nginx, vérifiez que les headers parviennent au backend. Si votre application enregistre les headers entrants, consultez-les :

sudo systemctl reload nginx
curl -s http://app.example.com/headers

Votre backend devrait voir X-Real-IP correspondant à l'IP du client, pas 127.0.0.1. Si vous voyez 127.0.0.1, les directives proxy_set_header sont absentes ou dans le mauvais contexte.

Note de sécurité : utilisez $proxy_add_x_forwarded_for au lieu de $remote_addr pour X-Forwarded-For. Cette variable ajoute l'IP du client au header X-Forwarded-For existant. Si vous n'utilisez que $remote_addr, vous perdez la chaîne de proxies, ce qui casse le suivi des IP derrière plusieurs proxies. Si Nginx est votre seul proxy, les deux sont équivalents.

Comment faire un reverse proxy pour une application Node.js ?

Voici un bloc serveur complet pour proxifier une application Node.js sur le port 3000. Cet exemple inclut le transfert de headers, le support WebSocket et le masquage de version.

Créez le bloc serveur :

sudo nano /etc/nginx/sites-available/nodeapp.conf
server {
    listen 80;
    server_name nodeapp.example.com;

    # Hide Nginx version in responses
    server_tokens off;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;

        # Header forwarding
        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;

        # WebSocket support
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Timeouts
        proxy_connect_timeout 60s;
        proxy_read_timeout 60s;
        proxy_send_timeout 60s;
    }
}

Activez, testez et rechargez :

sudo ln -s /etc/nginx/sites-available/nodeapp.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Vérifiez que le proxy fonctionne :

curl -I http://nodeapp.example.com

Point d'attention : la réponse ne devrait pas contenir de ligne Server: nginx/1.x.x. La directive server_tokens off masque le numéro de version. La divulgation de version aide les attaquants à cibler des vulnérabilités connues.

Pour la terminaison TLS avec Let's Encrypt, consultez Nginx SSL/TLS avec Let's Encrypt.

Comment proxifier les connexions WebSocket avec Nginx ?

Les connexions WebSocket démarrent par une requête HTTP avec un header Upgrade, puis passent à une connexion bidirectionnelle persistante. Nginx utilise HTTP/1.0 par défaut pour les connexions upstream, ce qui ne supporte pas le mécanisme Upgrade. Trois directives sont nécessaires : passer proxy_http_version à 1.1, transmettre le header Upgrade, et définir le header Connection à "upgrade".

Pour les locations qui servent à la fois du trafic HTTP classique et WebSocket, utilisez la directive map pour définir le header Connection de manière conditionnelle. Ajoutez ceci dans le bloc http de /etc/nginx/nginx.conf :

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

Ensuite référencez $connection_upgrade dans votre bloc serveur :

server {
    listen 80;
    server_name ws.example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        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-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # Prevent idle timeout killing WebSocket connections
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

Le proxy_read_timeout par défaut est de 60 secondes. Si aucune donnée ne transite pendant cet intervalle, Nginx ferme la connexion. Augmentez cette valeur pour les connexions WebSocket. Votre application devrait envoyer des frames WebSocket ping à un intervalle inférieur à ce timeout.

Testez la configuration :

sudo nginx -t && sudo systemctl reload nginx

Pour vérifier la connectivité WebSocket, installez wscat et connectez-vous :

npm install -g wscat
wscat -c ws://ws.example.com/

Si la connexion s'ouvre, le proxy WebSocket fonctionne. Si vous obtenez unexpected server response (200), les headers Upgrade ne sont pas transmis. Vérifiez que la directive map est dans le bloc http, pas dans un bloc server.

Comment faire un reverse proxy pour Ollama (IA auto-hébergée) ?

Ollama sert l'inférence LLM sur le port 11434 par défaut et écoute sur 127.0.0.1. Le proxifier via Nginx permet d'ajouter TLS, l'authentification et le contrôle d'accès sans modifier la configuration d'Ollama. La différence principale avec un proxy standard : l'inférence LLM peut prendre plusieurs minutes, et les réponses en streaming nécessitent la désactivation du buffering.

Créez le bloc serveur :

sudo nano /etc/nginx/sites-available/ollama.conf
server {
    listen 80;
    server_name ollama.example.com;

    server_tokens off;

    # Restrict access to specific IPs
    allow 192.168.1.0/24;
    allow 10.0.0.0/8;
    deny all;

    location / {
        proxy_pass http://127.0.0.1:11434;
        proxy_http_version 1.1;

        # Header forwarding
        proxy_set_header Host localhost:11434;
        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;
        proxy_set_header Connection '';

        # Disable buffering for streaming responses
        proxy_buffering off;
        proxy_cache off;
        chunked_transfer_encoding on;

        # Extended timeouts for LLM inference
        proxy_connect_timeout 300s;
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;
    }
}

Activez et testez :

sudo ln -s /etc/nginx/sites-available/ollama.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Vérifiez qu'Ollama répond via le proxy :

curl http://ollama.example.com/api/tags

Vous devriez voir une réponse JSON listant les modèles disponibles. Si vous obtenez un 403 Forbidden, l'IP de votre client n'est pas dans la liste allow. Si vous obtenez un 502 Bad Gateway, Ollama ne tourne pas :

sudo systemctl status ollama

Testez une complétion en streaming :

curl -N http://ollama.example.com/api/generate -d '{
  "model": "llama3.2",
  "prompt": "Hello",
  "stream": true
}'

Point d'attention : le flag -N désactive le buffering de sortie de curl. Vous devriez voir les tokens arriver un par un. Si la réponse entière arrive d'un coup, proxy_buffering off n'est pas défini ou est écrasé par un autre réglage.

Pourquoi ces réglages diffèrent d'un proxy standard :

  • proxy_buffering off : Nginx met normalement en tampon les réponses backend et les envoie en bloc. Pour le streaming LLM, chaque token doit être envoyé au client immédiatement.
  • proxy_read_timeout 600s : l'inférence LLM sur de grands modèles peut prendre plusieurs minutes. Le timeout par défaut de 60s couperait la connexion en cours de génération.
  • proxy_set_header Host localhost:11434 : Ollama vérifie le header Host et rejette les requêtes qui ne correspondent pas à son adresse d'écoute configurée.
  • proxy_set_header Connection '' : vide le header Connection pour éviter les problèmes de keep-alive avec le streaming chunked.

Pour la terminaison TLS afin d'exposer Ollama de manière sécurisée sur internet, consultez Nginx SSL/TLS avec Let's Encrypt. N'exposez jamais Ollama sans contrôle d'accès. Combinez les restrictions IP avec HTTP Basic Auth ou une validation par clé API en production.

Pour ajouter HTTP Basic Auth en plus des restrictions IP :

sudo apt install apache2-utils
sudo htpasswd -c /etc/nginx/.ollama_htpasswd apiuser
sudo chmod 640 /etc/nginx/.ollama_htpasswd
sudo chown root:www-data /etc/nginx/.ollama_htpasswd

Ajoutez ensuite dans le bloc location de ollama.conf :

    auth_basic "Ollama API";
    auth_basic_user_file /etc/nginx/.ollama_htpasswd;

Vérifiez que les permissions du fichier sont correctes :

ls -la /etc/nginx/.ollama_htpasswd

Le fichier devrait afficher -rw-r----- avec root comme propriétaire et www-data comme groupe. Restreindre les permissions empêche les autres utilisateurs du serveur de lire les hashes de mots de passe.

Comment configurer le load balancing upstream ?

Le bloc upstream définit un groupe de serveurs backend entre lesquels Nginx distribue les requêtes. Par défaut, Nginx utilise le round-robin pondéré. Chaque serveur reçoit une part de requêtes proportionnelle à son poids (poids par défaut : 1).

Ajoutez un bloc upstream avant votre bloc server :

upstream app_backends {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
}

server {
    listen 80;
    server_name app.example.com;

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

Méthodes de load balancing

Round-robin (par défaut) : les requêtes sont distribuées uniformément entre les serveurs. Aucune directive nécessaire.

Least connections (moins de connexions actives) : envoie les requêtes au serveur ayant le moins de connexions actives. Adapté aux backends avec des temps de réponse variables :

upstream app_backends {
    least_conn;
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
}

IP hash : associe chaque IP client à un backend spécifique. Utile pour la persistance de session sans cookie sticky :

upstream app_backends {
    ip_hash;
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
}

Health checks avec max_fails et fail_timeout

Nginx surveille passivement la santé des backends. Si un serveur ne répond pas, Nginx le marque comme indisponible et cesse de lui envoyer des requêtes pendant un certain temps.

upstream app_backends {
    server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3002 backup;
}
Paramètre Défaut Description
max_fails 1 Nombre d'échecs dans la fenêtre fail_timeout avant de marquer le serveur comme indisponible
fail_timeout 10s Fenêtre de comptage des échecs, et durée pendant laquelle le serveur reste marqué indisponible
backup - Le serveur ne reçoit des requêtes que quand tous les serveurs primaires sont hors service
weight 1 Part relative des requêtes en round-robin

Après configuration de l'upstream, testez et rechargez :

sudo nginx -t && sudo systemctl reload nginx

Vérifiez que le load balancing fonctionne en envoyant plusieurs requêtes et en identifiant quel backend répond :

for i in $(seq 1 6); do curl -s http://app.example.com/health; echo; done

Si vos backends renvoient un identifiant dans leur réponse, vous devriez voir les requêtes distribuées entre eux.

Comment régler le buffering et les timeouts du proxy ?

Nginx met en tampon les réponses backend par défaut. Il lit la réponse entière du backend en mémoire (ou sur disque si elle dépasse le tampon), puis l'envoie au client. C'est efficace pour la plupart des applications, mais inadapté pour le streaming, les Server-Sent Events (SSE) ou le long-polling.

Directives de timeout

Directive Défaut Recommandé Rôle
proxy_connect_timeout 60s 5-10s Temps pour établir la connexion au backend. Gardez-le court pour échouer vite.
proxy_read_timeout 60s 60-300s Temps d'attente de la réponse backend. Augmentez pour les API lentes.
proxy_send_timeout 60s 60s Temps pour envoyer le corps de la requête au backend. Augmentez pour les uploads volumineux.
location / {
    proxy_pass http://127.0.0.1:3000;

    proxy_connect_timeout 10s;
    proxy_read_timeout 120s;
    proxy_send_timeout 60s;
}

Ces timeouts s'appliquent entre deux opérations de lecture/écriture successives, pas sur la durée totale de la requête. Une réponse qui envoie des données toutes les 30 secondes ne déclenche jamais un proxy_read_timeout de 60 secondes.

Contrôles de buffering

Directive Défaut Description
proxy_buffering on Met en tampon la réponse complète du backend avant l'envoi au client
proxy_buffer_size 4k ou 8k Tampon pour la première partie de la réponse (headers)
proxy_buffers 8 4k ou 8 8k Nombre et taille des tampons pour le corps de la réponse
proxy_busy_buffers_size 8k ou 16k Taille maximale des tampons pouvant être occupés à envoyer au client

Quand désactiver le buffering du proxy ?

Désactivez le buffering quand le backend envoie des données en streaming : inférence LLM (Ollama, vLLM), Server-Sent Events, réponses longues de type WebSocket, ou toute API qui envoie des données chunked de manière incrémentale.

location /stream/ {
    proxy_pass http://127.0.0.1:8080;
    proxy_buffering off;
}

Gardez le buffering activé pour les API requête/réponse classiques. Le buffering protège votre backend des clients lents : Nginx absorbe la réponse rapidement et l'envoie au client à son rythme. Sans buffering, un client lent maintient une connexion backend ouverte.

Pour le tuning avancé des performances, consultez .

Comment résoudre les erreurs 502 et 504 ?

502 Bad Gateway signifie que Nginx n'a pas pu se connecter au backend ou que le backend a envoyé une réponse invalide. 504 Gateway Timeout signifie que le backend n'a pas répondu dans le délai proxy_read_timeout.

Checklist 502 Bad Gateway

  1. Le backend tourne-t-il ?
ss -tlnp | grep 3000

Si aucune sortie, votre backend n'écoute pas sur le port 3000. Démarrez-le.

  1. L'URL proxy_pass est-elle correcte ? Vérifiez les fautes de frappe dans le numéro de port ou l'adresse IP. Erreur fréquente : utiliser https:// pour un backend qui ne parle que HTTP.

  2. SELinux bloque-t-il la connexion ? (RHEL/CentOS)

sudo setsebool -P httpd_can_network_connect 1
  1. Consultez le log d'erreurs Nginx :
sudo tail -20 /var/log/nginx/error.log

Cherchez connect() failed (111: Connection refused) ou no live upstreams.

Checklist 504 Gateway Timeout

  1. Augmentez proxy_read_timeout :
proxy_read_timeout 300s;
  1. Vérifiez si le backend est lent :
time curl http://127.0.0.1:3000/slow-endpoint

Si cela prend plus longtemps que votre proxy_read_timeout, augmentez le timeout ou optimisez le backend.

  1. Vérifiez la santé de l'upstream : si vous utilisez des blocs upstream, tous les serveurs sont peut-être marqués comme en échec. Cherchez no live upstreams while connecting to upstream dans le log d'erreurs.

Lecture des logs Nginx

Nginx écrit les détails d'erreurs dans /var/log/nginx/error.log. Pour un suivi en temps réel :

sudo journalctl -u nginx -f

Ou surveillez le log d'erreurs directement :

sudo tail -f /var/log/nginx/error.log

Pour des logs par site, ajoutez les directives access_log et error_log dans votre bloc serveur :

server {
    listen 80;
    server_name app.example.com;

    access_log /var/log/nginx/app-access.log;
    error_log /var/log/nginx/app-error.log;

    location / {
        proxy_pass http://127.0.0.1:3000;
    }
}

Cela sépare les logs par application, ce qui facilite le débogage des problèmes avec un backend spécifique.

Problèmes fréquents de headers

Si votre backend reçoit 127.0.0.1 comme IP client au lieu de l'adresse réelle, la directive proxy_set_header X-Real-IP $remote_addr est manquante. Si votre application génère des liens HTTP avec http:// au lieu de https://, le header X-Forwarded-Proto n'est pas transmis. Certains frameworks (Express, Django, Rails) nécessitent une configuration explicite pour faire confiance aux headers de proxy. Dans Express :

app.set('trust proxy', 1);

Dans Django, configurez SECURE_PROXY_SSL_HEADER :

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

Sans ces réglages, votre application ignore les headers transmis même quand Nginx les envoie correctement.

Référence complète : directives proxy

Pour référence rapide, voici le jeu minimal de directives pour différents scénarios de proxy :

# Standard HTTP proxy
location / {
    proxy_pass http://127.0.0.1:3000;
    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;
}

# WebSocket proxy
location /ws/ {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_read_timeout 3600s;
}

# Streaming proxy (SSE, LLM)
location /stream/ {
    proxy_pass http://127.0.0.1:11434;
    proxy_http_version 1.1;
    proxy_buffering off;
    proxy_cache off;
    proxy_read_timeout 600s;
}

Pour les blocs serveur gérant plusieurs domaines, consultez Blocs serveur Nginx. Pour le hardening sécurité incluant le rate limiting et le contrôle d'accès, consultez .


Copyright 2026 Virtua.Cloud. Tous droits réservés. Ce contenu est une création originale de l'équipe Virtua.Cloud. Toute reproduction, republication ou redistribution sans autorisation écrite est interdite.

Prêt à essayer ?

Déployez votre serveur en quelques secondes. Linux, Windows ou FreeBSD.

Voir les offres VPS