TL;DR — Résumé Rapide

Configurez Nginx en proxy inverse et répartiteur de charge avec SSL. Couvre proxy_pass, Let's Encrypt, algorithmes de répartition, WebSocket et rate limiting.

Nginx alimente plus de 34 % des sites web les plus fréquentés au monde — et une grande partie de ce trafic le traverse non pas en tant que serveur web, mais en tant que proxy inverse et répartiteur de charge. Configurer Nginx comme proxy inverse avec terminaison SSL offre un point d’entrée unique durci qui répartit la charge entre les serveurs backend, décharge le TLS de votre application et ajoute le cache, le rate limiting et les en-têtes de sécurité en un seul endroit. Ce guide couvre chaque couche de cette architecture : fondamentaux du proxy inverse, terminaison SSL avec Let’s Encrypt, les quatre algorithmes de répartition, proxying WebSocket, cache des réponses, rate limiting, en-têtes de sécurité, HTTP/2 et HTTP/3, supervision et un scénario réel avec un cluster Node.js.

Prérequis

Avant de commencer, assurez-vous d’avoir :

  • Ubuntu 22.04 ou 24.04 (les commandes s’appliquent à toute distro basée sur Debian)
  • Nginx 1.18 ou ultérieur (sudo apt install nginx)
  • Un domaine enregistré pointant vers l’IP publique de votre serveur
  • Un accès terminal avec les privilèges sudo
  • Au moins deux serveurs ou processus d’application backend (pour la répartition de charge)

Fondamentaux du Proxy Inverse

Un proxy inverse se place entre internet et un ou plusieurs serveurs backend. Les clients se connectent à Nginx ; Nginx transmet la requête au backend approprié et retourne la réponse. Le backend n’a jamais besoin d’une IP publique.

La directive centrale est proxy_pass :

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

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

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Les quatre directives proxy_set_header sont indispensables dans toute configuration de production. Sans X-Forwarded-For, vos logs d’application n’affichent que l’IP du serveur Nginx. Sans X-Forwarded-Proto, votre application ne peut pas distinguer HTTP de HTTPS et peut générer des boucles de redirection incorrectes.

Définissez des délais raisonnables pour que les backends lents ne bloquent pas les workers de Nginx :

proxy_connect_timeout 10s;
proxy_send_timeout    60s;
proxy_read_timeout    60s;
proxy_buffering       on;
proxy_buffer_size     8k;
proxy_buffers         8 16k;

Terminaison SSL avec Let’s Encrypt

La terminaison SSL signifie que Nginx gère le handshake TLS et transmet du HTTP simple aux backends sur le réseau interne — sans surcharge TLS sur les serveurs d’application individuels.

Installez Certbot avec le plugin Nginx :

sudo apt install certbot python3-certbot-nginx -y

Obtenez et installez un certificat (Certbot modifie automatiquement votre bloc server) :

sudo certbot --nginx -d app.exemple.com -d www.app.exemple.com

Certbot crée un timer systemd qui renouvelle les certificats automatiquement avant leur expiration :

sudo systemctl status certbot.timer
sudo certbot renew --dry-run

Algorithmes de Répartition de Charge

Pour répartir la charge entre plusieurs backends, remplacez la cible unique de proxy_pass par un bloc upstream :

upstream app_cluster {
    server 10.0.0.10:3000;
    server 10.0.0.11:3000;
    server 10.0.0.12:3000;
}

Round-Robin (Par défaut)

Aucune directive nécessaire. Nginx distribue les requêtes séquentiellement entre tous les serveurs upstream. Ajoutez des poids pour diriger plus de trafic vers des nœuds plus puissants :

upstream app_cluster {
    server 10.0.0.10:3000 weight=3;
    server 10.0.0.11:3000 weight=1;
    server 10.0.0.12:3000 weight=1;
}

Moins de Connexions (least_conn)

Nginx route chaque nouvelle requête vers le serveur upstream ayant le moins de connexions actives. Idéal pour les charges de travail avec des temps de réponse variables :

upstream app_cluster {
    least_conn;
    server 10.0.0.10:3000;
    server 10.0.0.11:3000;
    server 10.0.0.12:3000;
}

IP Hash (ip_hash)

L’IP du client détermine quel backend répond à toutes les requêtes de ce client. Utile pour la persistance de session sans stockage de session partagé :

upstream app_cluster {
    ip_hash;
    server 10.0.0.10:3000;
    server 10.0.0.11:3000;
    server 10.0.0.12:3000;
}

Aléatoire (random)

Nginx sélectionne un backend aléatoirement. Avec le paramètre two, il choisit deux serveurs aléatoirement et route vers celui qui a le moins de connexions :

upstream app_cluster {
    random two least_conn;
    server 10.0.0.10:3000;
    server 10.0.0.11:3000;
    server 10.0.0.12:3000;
}

Vérifications de Santé des Upstreams

Marquez les serveurs comme backup ou définissez des seuils d’échec pour que Nginx cesse d’envoyer du trafic aux backends indisponibles :

upstream app_cluster {
    least_conn;
    server 10.0.0.10:3000 max_fails=3 fail_timeout=30s;
    server 10.0.0.11:3000 max_fails=3 fail_timeout=30s;
    server 10.0.0.12:3000 backup;
}

Proxy WebSocket

Les connexions WebSocket nécessitent un handshake d’upgrade HTTP/1.1. Ajoutez trois directives à tout bloc location qui proxy du trafic WebSocket :

location /ws/ {
    proxy_pass http://app_cluster;

    proxy_http_version 1.1;
    proxy_set_header Upgrade    $http_upgrade;
    proxy_set_header Connection "upgrade";

    proxy_read_timeout 3600s;

    proxy_set_header Host              $host;
    proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Cache avec proxy_cache

Nginx peut mettre en cache les réponses des backends en mémoire ou sur disque, réduisant considérablement la charge sur les backends pour les endpoints cacheables.

Définissez une zone de cache dans le contexte http :

http {
    proxy_cache_path /var/cache/nginx
                     levels=1:2
                     keys_zone=app_cache:10m
                     max_size=1g
                     inactive=60m
                     use_temp_path=off;
}

Activez le cache dans le bloc location :

location /api/public/ {
    proxy_pass         http://app_cluster;
    proxy_cache        app_cache;
    proxy_cache_valid  200 10m;
    proxy_cache_valid  404  1m;
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
    add_header         X-Cache-Status $upstream_cache_status;
}

Rate Limiting

Le rate limiting protège les backends des pics de trafic et des attaques par force brute. Définissez une zone de mémoire partagée dans le contexte http :

http {
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=30r/m;
}

Appliquez la zone à un location :

location /api/ {
    limit_req zone=api_limit burst=10 nodelay;
    limit_req_status 429;
    proxy_pass http://app_cluster;
}

En-têtes de Sécurité

Ajoutez des en-têtes de sécurité dans le bloc server :

server {
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
    add_header Strict-Transport-Security "max-age=15768000; includeSubDomains" always;
    add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';" always;
    server_tokens off;
}

HTTP/2 et HTTP/3

Activez HTTP/2 dans la directive listen :

listen 443 ssl http2;

Pour HTTP/3 (Nginx 1.25+) :

listen 443 ssl http2;
listen 443 quic reuseport;
add_header Alt-Svc 'h3=":443"; ma=86400' always;

Supervision avec stub_status

Activez le module stub_status pour exposer des métriques de connexion en temps réel :

server {
    listen 127.0.0.1:8080;

    location /nginx_status {
        stub_status;
        allow 127.0.0.1;
        deny all;
    }
}
curl http://127.0.0.1:8080/nginx_status

Comparaison : Nginx vs. Alternatives

FonctionnalitéNginxHAProxyTraefikCaddy
Protocoles supportésHTTP, HTTPS, TCP, UDPHTTP, HTTPS, TCPHTTP, HTTPS, TCP, gRPCHTTP, HTTPS
Algorithmes de répartitionRR, LC, IP hash, randomRR, LC, source, URIRR, WRRRR, LC
Health checks actifsNginx Plus uniquementOui (natif)Oui (natif)Oui (natif)
SSL automatique (ACME)Via CertbotVia CertbotNatifNatif
Cache intégréOuiNonNonNon
HTTP/31.25+ (expérimental)NonNonOui
Meilleur pourWeb + proxy + cacheLB TCP/HTTP purIngress KubernetesSSL automatique simple

Scénario Réel : Répartition de Charge d’un Cluster Node.js avec SSL

Vous avez une API Node.js de production exécutant trois processus PM2 sur le même hôte (ports 3001, 3002, 3003) et souhaitez les exposer en tant que api.exemple.com via HTTPS avec affinité de session, rate limiting sur le endpoint d’authentification et support WebSocket.

upstream node_api {
    ip_hash;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
    server 127.0.0.1:3003;
}

limit_req_zone $binary_remote_addr zone=auth_limit:5m  rate=5r/m;
limit_req_zone $binary_remote_addr zone=api_limit:10m  rate=60r/m;

server {
    listen 443 ssl http2;
    server_name api.exemple.com;

    ssl_certificate     /etc/letsencrypt/live/api.exemple.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.exemple.com/privkey.pem;
    include             /etc/letsencrypt/options-ssl-nginx.conf;

    add_header Strict-Transport-Security "max-age=15768000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    server_tokens off;

    location /api/ {
        limit_req zone=api_limit burst=20 nodelay;
        limit_req_status 429;
        proxy_pass         http://node_api;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /api/auth/ {
        limit_req zone=auth_limit burst=3 nodelay;
        limit_req_status 429;
        proxy_pass         http://node_api;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /ws/ {
        proxy_pass         http://node_api;
        proxy_http_version 1.1;
        proxy_set_header Upgrade    $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host              $host;
        proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_read_timeout 3600s;
    }
}

server {
    listen 80;
    server_name api.exemple.com;
    return 301 https://$host$request_uri;
}

Pièges et Cas Particuliers

Le slash final dans proxy_pass est important. proxy_pass http://backend; conserve l’URI complet incluant le préfixe /api/. proxy_pass http://backend/; supprime le préfixe du location et peut causer des erreurs 404 difficiles à diagnostiquer.

N’utilisez pas ip_hash si un CDN est devant Nginx. Tout le trafic CDN semble provenir d’un petit ensemble d’IPs, donc ip_hash enverra tout ce trafic vers un seul backend.

Les connexions longues et SSE nécessitent des délais plus grands. Les connexions Server-Sent Events et le long-polling restent ouvertes indéfiniment. Ajustez proxy_read_timeout à l’intervalle de heartbeat de votre application plus une marge.

proxy_buffering et streaming. Si votre backend diffuse une réponse en streaming (SSE, transfert chunked), définissez proxy_buffering off sur ce location. Sinon Nginx met en buffer la réponse complète avant de l’envoyer, annulant l’intérêt du streaming.

Dépannage

502 Bad Gateway. Le backend n’est pas en cours d’exécution ou n’écoute pas sur le port configuré. Vérifiez : sudo systemctl status votre-app, ss -tlnp | grep 3000.

504 Gateway Timeout. Le backend répond trop lentement. Augmentez proxy_read_timeout ou examinez les performances du backend.

Les connexions WebSocket tombent après 60 secondes. Le proxy_read_timeout par défaut de Nginx est 60 secondes. Augmentez-le pour les locations WebSocket.

Le cache ne fonctionne pas. Assurez-vous que proxy_cache_path est défini dans le contexte http, que le répertoire existe et est accessible en écriture pour l’utilisateur nginx, et que proxy_cache est configuré dans le bon bloc location.

Résumé

Nginx comme proxy inverse et répartiteur de charge fournit une couche de trafic de niveau production sans infrastructure supplémentaire. Points clés :

  • Définissez toujours X-Forwarded-For, X-Forwarded-Proto et Host dans chaque location de proxy
  • Utilisez certbot --nginx pour provisionner des certificats SSL sans interruption et avec renouvellement automatique
  • Choisissez least_conn pour les APIs, ip_hash pour la persistance de session et random two least_conn pour les grands pools de backends
  • Marquez les backends avec max_fails et fail_timeout pour que Nginx retire automatiquement les serveurs indisponibles
  • Utilisez proxy_http_version 1.1 avec les en-têtes Upgrade/Connection pour le proxy WebSocket
  • Activez proxy_cache pour les endpoints publics et limit_req_zone pour protéger les routes d’authentification
  • Activez http2 dans la directive listen pour le multiplexage ; évaluez quic sur Nginx 1.25+

Articles Connexes