Guide complet du reverse proxy Nginx : upstream, mise en tampon et timeouts

Le téléphone vibre sans arrêt — alerte production.
Les logs sont pleins de 502 Bad Gateway. Le backend tourne, mais les timeouts Nginx sont trop courts : au pic de trafic, les requêtes sont coupées avant la fin du traitement. Ce proxy_read_timeout 60s avait été posé à la va-vite.
Après cet incident, une semaine à creuser les trois modules clés du reverse proxy Nginx : équilibrage upstream, mise en tampon proxy buffer et configuration des timeouts. Bien réglés, le reverse proxy encaisse dix fois plus de trafic ; mal réglés, c’est l’alerte de 3 h du matin.
Cet article rassemble les pièges, le debug et la logique derrière chaque paramètre. Backend, ops ou simple curiosité sur Nginx — vous devriez gagner du temps.
Upstream et équilibrage de charge : bien plus que « distribuer des requêtes »
Syntaxe de base
Le bloc upstream est le cœur de l’équilibrage Nginx. La forme minimale :
upstream backend {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
server {
location / {
proxy_pass http://backend;
}
}
Simple en surface — un groupe de backends, un proxy_pass. En production, il faut aussi : que faire si un serveur tombe ? Donner plus de trafic aux machines plus puissantes ? Maintenir les longues connexions ?
Quatre algorithmes, quatre cas d’usage
Par défaut, Nginx utilise le round-robin : distribution séquentielle. Équitable, pas intelligent.
Pour les longues connexions — WebSocket, pools de connexions DB — le round-robin peut faire exploser les connexions sur une machine. least_conn convient mieux :
upstream backend {
least_conn;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
Nginx suit les connexions actives par serveur et envoie la nouvelle requête au moins chargé. Sur un projet WebSocket temps réel, le round-robin avait fait monter la RAM d’un nœud — least_conn a uniformisé la charge.
Autre cas : après login, les requêtes doivent retourner sur le même serveur (session locale). ip_hash sert à ça :
upstream backend {
ip_hash;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
Même IP client → même backend. Limite : si ce serveur tombe, la session disparaît. Mieux vaut Redis pour les sessions ; ip_hash reste une solution temporaire.
Quatrième option : hash cohérent, fréquent pour le cache distribué :
upstream backend {
hash $request_uri consistent;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
Nginx crée 160 nœuds virtuels par unité de poids et route selon le hash de l’URI. Même URI → même machine, meilleur taux de hit cache.
Pondération : machines hétérogènes
Configurations inégales : 32 Go / 8 cœurs d’un côté, 16 Go / 4 cœurs de l’autre. Le round-robin équitable gaspille la puissance.
upstream backend {
server 192.168.1.10:8080 weight=3;
server 192.168.1.11:8080 weight=2;
server 192.168.1.12:8080 weight=1;
}
weight=3 reçoit trois fois plus de requêtes. Les machines capables travaillent plus — logique.
Paramètre backup pour le serveur de secours :
upstream backend {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080 backup;
}
Le backup n’entre en jeu que si les autres sont down — le remplaçant sur le banc.
Pool keepalive : le secret du doublement de perf
Souvent négligé. Par défaut, Nginx ouvre une TCP vers le backend à chaque requête et la ferme après la réponse.
Coût élevé : handshake à l’ouverture, fermeture en quatre temps. En forte concurrence, le pool keepalive réutilise les connexions.
Exemple :
upstream backend {
server 192.168.1.10:8080;
keepalive 32; # 32 connexions idle max par worker
}
server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
Deux points :
keepalive 32: connexions idle max par worker- Obligatoire :
proxy_http_version 1.1etConnection ""— HTTP/1.0 ne gère pas la persistance
Test API : sans keepalive ~2000 QPS, avec keepalive 4000+. Le double n’est pas une exagération.
Attention : ne montez pas keepalive trop haut. En test, 100 connexions sur un seul conteneur ECS ont saturé le backend. Formule prod :
keepalive ≈ QPS total ÷ durée moyenne par requête ÷ nombre de workers
Ex. QPS 10000, latence 50 ms, 4 workers :
10000 × 0.05 ÷ 4 = 125
~125 est raisonnable.
Contrôle de santé : retrait automatique des backends down
La version open source ne propose que le contrôle passif — marquage après échec de requête :
upstream backend {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
server {
location / {
proxy_pass http://backend;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
}
}
proxy_next_upstream : retry sur erreur connexion, timeout ou 502/503/504. proxy_next_upstream_tries 3 : max 3 backends testés.
Limite : détection tardive — il faut un échec réel. Pour une haute dispo, le contrôle actif NGINX Plus est préférable :
upstream backend {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
server {
location / {
proxy_pass http://backend;
health_check interval=5s fails=3 passes=2;
}
}
Sonde toutes les 5 s ; 3 échecs → unhealthy ; 2 succès → retour healthy.
Proxy Buffer : allié ou obstacle ?
À quoi sert le buffering
Nginx ne renvoie pas la réponse backend directement au client : elle passe d’abord par un tampon.
Pourquoi ? La vitesse client est imprévisible. Le backend peut envoyer vite ; si le client est lent, Nginx attend. Avec un buffer, Nginx stocke la réponse et la sert progressivement — le backend peut traiter la requête suivante plus tôt.
Contrepartie : consommation mémoire, surtout gros corps de réponse et forte concurrence.
Trois paramètres clés
proxy_buffer_size 4k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
Au début, les noms se ressemblent. En résumé :
proxy_buffer_size: tampon des en-têtes de réponse, un par requêteproxy_buffers: tableau pour le corps ; formatnombre taille_unitaireproxy_busy_buffers_size: portion en cours d’envoi client ; max la moitié du total buffers
Ex. proxy_buffers 8 32k → 256k total. proxy_busy_buffers_size 64k = un quart, conforme.
Quand ajuster ?
En-têtes énormes (beaucoup de cookies) → erreur « upstream sent too big header ». Augmentez proxy_buffer_size :
proxy_buffer_size 16k;
Gros JSON en réponse → agrandissez les buffers :
proxy_buffers 16 64k;
Cas où il faut couper le buffering
Server-Sent Events (SSE) : flux d’événements continu ; le buffering retarde le client :
location /events {
proxy_pass http://backend;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 86400s;
}
proxy_read_timeout 86400s (24 h) : connexion longue, pas de coupure par timeout.
WebSocket : communication bidirectionnelle temps réel :
location /ws {
proxy_pass http://backend;
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400s;
}
Upload volumineux : 1 Go côté client ; tout bufferiser avant forward → explosion RAM. Désactivez le buffering requête :
location /upload {
proxy_pass http://backend;
proxy_request_buffering off;
client_max_body_size 1G;
}
proxy_request_buffering off : transfert en flux, réception et envoi simultanés.
Timeouts : la logique derrière les paramètres
Trois timeouts, trois rôles
proxy_connect_timeout 10s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
Noms proches, fonctions distinctes :
proxy_connect_timeout: délai pour établir la TCP vers le backend (réseau lent, firewall…)proxy_read_timeout: après connexion, délai entre deux lectures de données backendproxy_send_timeout: limite pour envoyer le corps de requête au backend
Piège courant : proxy_read_timeout n’est pas un timeout total, mais l’intervalle entre deux lectures. Traitement 5 min avec heartbeats réguliers → 60s peut suffire. Silence total 5 min → proxy_read_timeout 300s.
Lien avec 502 et 504
Leçon de l’alerte de 3 h :
- 502 Bad Gateway : Nginx n’atteint pas le backend — service down, port fermé, firewall
- 504 Gateway Timeout : connecté, mais pas de données assez vite
Ex. proxy_connect_timeout 10s : backend met 15 s à accepter → 502. Connexion rapide, traitement 2 min, proxy_read_timeout 60s → 504.
Stratégies par scénario
API : 30–60 s en général. Timeout court pour repérer les lenteurs :
proxy_connect_timeout 5s;
proxy_read_timeout 30s;
proxy_send_timeout 30s;
Traitement fichiers : export, PDF — plusieurs minutes possibles :
proxy_connect_timeout 10s;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
Flux longs : live, WebSocket, SSE — 24 h est normal :
proxy_read_timeout 86400s;
Dépannage 502/504 en pratique
Causes fréquentes
Cas rencontrés :
- Backend réellement down : crash, port occupé, OOM
- Pool de connexions saturé : backend plein, Nginx ne se connecte plus
- Timeouts trop courts : comme l’alerte de 3 h — 60s alors que le backend met 2 min
- Réseau / firewall : règles sécurité, iptables
Diagnostic via les logs
Première étape : error_log :
error_log /var/log/nginx/error.log warn;
Messages typiques :
upstream timed out (110: Connection timed out) while reading response header from upstream
504 — timeout de lecture.
connect() failed (111: Connection refused) while connecting to upstream
502 — connexion refusée, rien n’écoute.
Format de log personnalisé pour l’état upstream :
log_format upstream_status '$status $upstream_status $upstream_response_time';
access_log /var/log/nginx/access.log upstream_status;
Sortie du type 200 200, 200, 502 0.5, 1.2, 3.0 — statut et latence par backend.
Solutions types
Scénario 1 : backend lent, 504 fréquents
Augmentez proxy_read_timeout et vérifiez que le backend termine vraiment. Ajustez aussi les timeouts côté application, pas seulement Nginx.
Scénario 2 : connexion refusée, 502
Vérifiez processus, port en écoute, règles firewall :
netstat -tlnp | grep 8080
ps aux | grep your_app
Scénario 3 : saturation connexions en pic
Augmentez les limites backend ou activez keepalive upstream pour réduire la création de connexions.
Bonnes pratiques de performance
Configuration worker
Nginx est multi-processus. worker_processes = nombre de processus, souvent égal aux cœurs CPU :
worker_processes auto;
auto détecte les cœurs. Machine 8 cœurs → 8 workers.
worker_connections : connexions max par worker :
events {
worker_connections 4096;
}
Concurrence théorique = worker_processes × worker_connections. 8 × 4096 = 32768. Limité aussi par les descripteurs de fichiers système.
Trois réglages TCP
sendfile on;
tcp_nopush on;
tcp_nodelay on;
Combinaison performante :
sendfile on: transfert fichier via le noyau, sans copie user-spacetcp_nopush on: avec sendfile, envoi par lots plutôt que paquet par paquettcp_nodelay on: petits paquets envoyés tout de suite
Fichiers statiques : débit +30 % mesuré avec ces trois options.
Autres optimisations
Compression gzip : texte compressé, bande passante économisée :
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 1024;
Limites descripteurs : en forte concurrence, ulimit peut bloquer :
ulimit -n
Si 1024 seulement, augmentez dans /etc/security/limits.conf :
* soft nofile 65535
* hard nofile 65535
Exemple de configuration complète
Modèle recommandé pour la production :
# Configuration de base
worker_processes auto;
events {
worker_connections 4096;
multi_accept on;
}
http {
# Optimisation TCP
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# Keepalive
keepalive_timeout 30;
keepalive_requests 100;
# Buffers
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
# Timeouts
proxy_connect_timeout 10s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
# gzip
gzip on;
gzip_types text/plain text/css application/json;
upstream backend {
least_conn;
server 192.168.1.10:8080 weight=3;
server 192.168.1.11:8080 weight=2;
server 192.168.1.12:8080 backup;
keepalive 32;
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
}
# SSE
location /events {
proxy_pass http://backend;
proxy_buffering off;
proxy_read_timeout 86400s;
}
}
}
Conclusion
Trois piliers :
- Upstream : bon algorithme, pool keepalive, contrôle de santé
- Buffers : comprendre les trois paramètres ; couper le buffering si besoin
- Timeouts : rôle de chaque directive ; stratégie selon le scénario
L’alerte de 3 h m’a appris : la config Nginx n’est pas du remplissage de paramètres. Chaque valeur a une logique — la comprendre évite les pièges.
Débutant ? Partez des défauts, ajustez au problème — pas un proxy_read_timeout 60s posé au hasard en prod. Déjà passé par là ? Ce guide devrait relier vos bouts d’expérience.
En relisant ma prod actuelle : keepalive 32, proxy_read_timeout 120s, least_conn. Plus d’alerte à 3 h du matin.
FAQ
proxy_read_timeout est-il un timeout total ou l'intervalle entre deux lectures ?
Quand faut-il désactiver proxy_buffering ?
• Server-Sent Events (SSE) : push temps réel, le buffering retarde les messages
• WebSocket : communication bidirectionnelle temps réel, flux continu requis
• Upload de gros fichiers : éviter l'explosion mémoire, transférer au fil de l'eau
Quelle valeur pour keepalive ?
Quelle différence entre 502 et 504 ?
Quel algorithme d'équilibrage choisir ?
• Round-robin (défaut) : services sans état, répartition équitable
• least_conn : longues connexions (WebSocket, pools DB)
• ip_hash : affinité de session (solution temporaire, Redis préférable)
• hash : cache distribué, meilleur taux de hit
Comment résoudre upstream sent too big header ?
Pourquoi sendfile + tcp_nopush + tcp_nodelay améliorent les performances ?
8 min de lecture · Publié le: 30 mars 2026 · Mis à jour le: 27 juil. 2026
Guide pratique Nginx
Vous lisez le premier article de cette série. Continuez avec le suivant ou ouvrez le hub de la série pour voir tout le parcours.
Précédent
Vous êtes au début de cette série.
Suivant
Optimisation des performances Nginx : gzip, cache et pool de connexions
Guide détaillé des réglages clés pour optimiser Nginx : compression gzip (-60 à -80 % de volume), stratégie proxy_cache (jusqu'à 95 % de hits), worker_connections et pools de connexions (×3 à ×4 en concurrence)
Partie 2 sur 6



Commentaires
Connectez-vous avec GitHub pour laisser un commentaire