Je configure la fonctionnalité « Upstream Keepalive » de NGINX de manière à ce que le proxy inverse établisse moins de connexions, offre des latences plus faibles et gère de manière fiable les pics de charge. Pour cela, je règle Taille de la piscine, les délais d'expiration et les en-têtes de manière ciblée, afin que les connexions soient réutilisées et que le chemin de données reste allégé.
Points centraux
- HTTP/1.1 Forcer et nettoyer les en-têtes de connexion
- keepalive dimensionner correctement pour chaque travailleur
- Timeouts adapter aux valeurs du backend
- Requêtes/Connexion réduire et recycler
- Suivi pour le débit de connexion et la latence
Pourquoi Upstream Keepalive réduit considérablement le nombre de connexions établies
Sans réutilisation, NGINX ouvre une nouvelle connexion backend à chaque requête, ce qui entraîne des handshakes supplémentaires, une augmentation de la charge CPU et une consommation accrue de ressources du noyau ; c'est précisément là qu'intervient Keepalive . Je configure NGINX pour qu'il mette en cache les sockets déjà établies mais actuellement inactives et les réutilise pour les requêtes suivantes, ce qui réduit sensiblement les temps de connexion. Cela diminue le nombre de connexions par seconde, réduit les pics de backlog et limite les changements de contexte au niveau du système d'exploitation. C’est notamment avec le protocole TLS vers le backend que je gagne un temps considérable grâce à la réutilisation des sessions. Ainsi, la chaîne de réponses reste stable même en cas de débit élevé fiable et réagit avec souplesse.
Principe de base et directive « keepalive » dans l'upstream
La directive keepalive Dans le bloc „ upstream “, cela limite le nombre de connexions backend inactives mises en cache par worker. Cette limite ne s'applique pas globalement, mais strictement à chaque processus worker ; c'est pourquoi je surveille en permanence le nombre de workers. Lorsque le pool est plein, NGINX ferme d'abord la connexion inactive depuis le plus longtemps afin de libérer de l'espace pour de nouveaux sockets. Pour la réutilisation, le côté proxy nécessite HTTP/1.1 et un en-tête « Connection » neutralisé. Sans ces conditions préalables, le pool reste vide, même si je définis « keepalive » dans l'upstream, ce que de nombreux administrateurs au début surpris.
upstream backend_pool {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
keepalive 32 ; # connexions inactives par worker
keepalive_requests 1000 ; # recyclage après N requêtes
keepalive_timeout 60s ; # durée de vie en inactivité
}
server {
listen 80;
location / {
proxy_pass http://backend_pool;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
Directives obligatoires dans le bloc « Location » : HTTP/1.1 et contrôle des en-têtes
Je force NGINX à utiliser HTTP/1.1 dans le chemin du proxy, car Keepalive ne fonctionne pas correctement avec HTTP/1.0 et les connexions se terminent inutilement ; la directive proxy_http_version 1.1 est donc obligatoire. De plus, je supprime l'en-tête „ Connection “ pour les requêtes classiques, afin que le backend ne reçoive pas d'instruction „ close “. Pour les mises à niveau telles que les WebSockets, j’utilise « Connection: Upgrade » de manière ciblée via une fonction map, sans affecter la réutilisation normale. Ainsi, la politique de connexion reste cohérente et découplée des en-têtes client. C’est précisément cette petite modification qui évite de nombreux problèmes difficiles à cerner erreurs types.
location / {
proxy_pass http://backend_pool;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
map $http_upgrade $connection_upgrade {
default upgrade;
"" "";
}
Réglage fin : choisir correctement les valeurs de « keepalive_requests » et « keepalive_timeout »
Grâce à deux paramètres, je contrôle la durée de vie et le renouvellement des connexions, afin que la liste reste à jour et qu’aucun socket orphelin ne vienne perturber le fonctionnement ; il s’agit de keepalive_requests et keepalive_timeout. Après N requêtes, NGINX ferme la connexion de manière ciblée et la rétablit si nécessaire, ce qui atténue les effets de vieillissement du réseau. Je règle le délai d'inactivité (Idle Timeout) plutôt court, généralement entre 30 et 120 secondes, afin que les serveurs backend ne coupent pas la connexion avant l'heure. Le réglage est essentiel : la valeur NGINX ne doit jamais dépasser le délai d’expiration des serveurs d’applications, sinon les réinitialisations de connexion s’accumulent. Ceux qui souhaitent approfondir le sujet trouveront des conseils pratiques dans l’article Délai d'expiration du keepalive, qui explique les valeurs typiques et les interactions.
Pour vous permettre de vous y retrouver rapidement, je vous présente ci-dessous les valeurs par défaut courantes et leur utilité respective dans un tableau clair Tableau. Ces valeurs indicatives servent de point de départ et, après suivi, s'avèrent souvent légèrement supérieures ou inférieures. Un délai trop court entraîne des réinitialisations inutiles, tandis qu’un délai trop long maintient d’anciennes connexions. Je protège le nombre de requêtes par connexion contre les valeurs aberrantes, sans pour autant vider le pool. Grâce à ces paramètres clés, je parviens très rapidement à obtenir des résultats fonctionnels Défauts.
| Paramètres | Objectif | valeur indicative | Remarque concernant le tuning |
|---|---|---|---|
| keepalive | Taille du pool « idle » par worker | 32-64 | S'aligner sur la charge simultanée par worker |
| keepalive_requests | Nombre maximal de requêtes par connexion | 500–1000 | Pour les diffusions longues, réglez-le un peu plus haut |
| keepalive_timeout | Durée maximale d'inactivité par connexion | les années 60 | Plus court ou identique au délai d'inactivité du backend |
Déterminer la taille du pool en fonction du nombre de connexions simultanées
Je ne choisis pas la taille du pool en fonction du nombre de requêtes par seconde, mais en fonction de Concurrence par worker. Je commence par déterminer le nombre moyen et maximal de requêtes backend parallèles. Ensuite, je divise ces chiffres par le nombre de workers NGINX et j'arrondis à l'unité supérieure. Pour 200 requêtes simultanées avec quatre workers, j’obtiens environ 50 par worker, ce qui fait que la valeur initiale de keepalive 64 convient. Cela me permet de maintenir les sockets disponibles sans ouvrir inutilement un trop grand nombre de Connexions de se lier.
Exploiter à bon escient les fonctionnalités des dernières versions de NGINX
Les versions actuelles autorisent souvent la réutilisation par défaut, mais fixent des limites plutôt prudentes ; j'indique tout de même les valeurs explicitement . Cela garantit la reproductibilité, facilite le réglage et évite les surprises après une mise à jour. Le paramètre „ local “ me permet, si je le souhaite, de limiter la réutilisation à un emplacement spécifique lorsque les profils de sécurité ou les politiques d’en-têtes diffèrent. La séparation reste ainsi claire, sans pour autant perdre globalement les avantages de la réutilisation. En définissant des valeurs claires, je documente mes intentions et gagne du temps par la suite Durée de l'analyse.
Suivi et indicateurs : la configuration est-elle vraiment efficace ?
Je commence par vérifier le nombre de nouvelles connexions au backend par seconde ; une baisse significative indique que les mesures prises sont efficaces. Réutilisation. J’observe ensuite la valeur « upstream_connect_time », qui est proche de zéro lorsque les requêtes sont traitées dans le pool. Les erreurs dans les journaux, en particulier les réinitialisations de connexion, indiquent des délais d’expiration qui sont à l’origine des valeurs du backend. De plus, je mets en corrélation l’utilisation du CPU des backends et les latences avec le pourcentage de connexions réutilisées. Pour une meilleure compréhension de la Réutilisation des connexions Des exemples illustrant les effets selon différents profils de charge sont utiles.
Éliminer rapidement les sources d'erreurs courantes
Si le protocole HTTP/1.1 n'est pas disponible côté backend, les connexions restent éphémères, quel que soit le niveau de keepalive . Lorsque le client envoie „ Connection: close “ et que je transmets l'en-tête sans le filtrer, le backend libère chaque connexion immédiatement après la réponse. Si les délais d'inactivité ne correspondent pas, c'est le côté application qui met fin à la connexion en premier et NGINX subit une réinitialisation lors de la requête suivante. Un pool surdimensionné maintient trop de sockets ouverts et gaspille de la mémoire et des ports. Je vérifie ces quatre points lors de chaque analyse en tant que Premièrement, car elles expliquent à 90 % tous les problèmes.
Exemple pratique : configuration de référence pour un débit élevé
En quelques instructions seulement, je parviens à rendre un proxy fortement sollicité rapide et fiable, tout en garantissant un transfert correct des en-têtes ; le modèle suivant a fait ses preuves et peut être facilement adapter. Je règle le keepalive sur 64, je limite le nombre de requêtes par connexion à 1 000 et je maintiens un délai d'inactivité de 60 secondes. De plus, je transmets correctement les informations d'hôte et de transfert afin que les backends puissent appliquer leur logique et la limitation de débit. Cette combinaison préserve le CPU, réduit les temps de réponse et permet de gérer les pics de charge avec plus de sérénité. C’est exactement ainsi que j’obtiens une Performance.
upstream app_backend {
server 10.0.1.10:3000 max_fails=2 fail_timeout=30s;
server 10.0.1.11:3000 max_fails=2 fail_timeout=30s;
keepalive 64;
keepalive_requests 1000;
keepalive_timeout 60s;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://app_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
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 environnements d'hébergement et les aspects opérationnels qui comptent vraiment
Je place souvent NGINX en amont de PHP-FPM, de Node.js ou de services Java, en veillant à ce que les latences réseau restent faibles et que les délais d'expiration du backend soient cohérents ; cela permet d'obtenir Planification. Une configuration réseau solide au niveau du noyau, avec des limites de sockets adaptées, empêche les collisions entre un grand nombre de connexions ouvertes. Une répartition équilibrée de la charge CPU et des chemins d’accès rapides au stockage aident les backends à maintenir des temps de réponse courts. De plus, je veille à ce que les configurations soient gérées par versions afin que les modifications restent traçables. Grâce à cette rigueur, le système reste stable même lors des pics de trafic réactif.
Meilleures pratiques pour les opérations courantes
Je commence avec un keepalive de 32 à 64, 500 à 1 000 requêtes par connexion et un temps d'inactivité de 60 secondes, puis je procède à des mesures systématiques et j'ajuste les valeurs ; cela permet d'obtenir rapidement succès. J'accompagne chaque modification de mesures relatives au taux de connexion, à la latence et aux schémas d'erreurs, jusqu'à ce que les courbes se stabilisent. J'adapte la taille du pool en fonction du nombre de requêtes simultanées, et non du débit brut par seconde. Les délais d’expiration ne dépassent jamais ceux de la pile backend, sous peine de provoquer des réinitialisations sporadiques. Si vous souhaitez affiner davantage les réglages, vous trouverez des conseils de réglage précis sous Optimiser les requêtes Keepalive, ce qui rend le recyclage assez facile à gérer.
Synchronisation des délais d'expiration du proxy et du keepalive TCP
Outre les paramètres de keepalive proprement dits, je règle avec précision les délais de transport. Le trio composé de proxy_connect_timeout, proxy_send_timeout et proxy_read_timeout détermine le niveau de patience de NGINX lors de la construction, de l'envoi et de la réception. Je ne définis jamais ces valeurs à un niveau supérieur à celui de leurs équivalents dans le backend, mais légèrement en dessous, afin que les erreurs soient détectées rapidement et ne s'aggravent pas côté application. De plus, j'active proxy_socket_keepalive, afin que le système d'exploitation envoie régulièrement des signaux de vie concernant les sockets inactifs et détecte les connexions semi-ouvertes. Cela évite que des connexions inactives ne restent dans le pool et ne provoquent des pics de latence lors de la prochaine requête.
server {
listen 80;
location / {
proxy_pass http://backend_pool;
proxy_connect_timeout 3s; # : abandon rapide si aucune connexion n'est possible
proxy_send_timeout 30s; # Écriture vers le backend
proxy_read_timeout 30s; # Réponses du backend
proxy_socket_keepalive on; # Activer le keepalive TCP du système d'exploitation
}
}
Pour les flux de longue durée (par exemple, SSE ou WebSockets), j'augmente uniquement le délai d'attente de lecture, tandis que la connexion reste établie. Cela me permet de réagir rapidement aux cibles défaillantes, tout en laissant passer sans interruption les réponses légitimes, même longues.
Planification des ressources : worker_connections, FD et ports éphémères
Un pool de keepalive propre ne sert à rien si les limites des descripteurs de fichiers ou les plages de ports sont épuisées. Je prévois donc worker_connections et worker_rlimit_nofile avec une marge de sécurité. À titre indicatif, je calcule : FDs ouverts ≈ (connexions client simultanées + connexions backend simultanées + sockets inactives mises en pool) par worker. Si j’utilise plusieurs upstreams avec des pools, les besoins se multiplient. Je prête également attention à la plage de ports éphémères du système, car NGINX agit en tant que client TCP vers le backend et accumule des états TIME_WAIT.
worker_processes auto;
worker_rlimit_nofile 131072;
events {
worker_connections 8192;
}
Exemples Linux # (sysctl) :
net.core.somaxconn = 4096
net.ipv4.ip_local_port_range = 10240 65535
net.ipv4.tcp_fin_timeout = 15
J'adopte une approche prudente : je ne supprime pas TIME_WAIT de manière trop radicale, mais je réduis le taux de connexion via les keepalives. Ainsi, les paramètres du noyau restent non critiques et le comportement reste prévisible.
Zones en amont, stratégie d'équilibrage de charge et rotation DNS
S'il y a plusieurs workers, je partage l'état du balancer via une zone, afin que les pannes et les charges restent cohérentes. Les sockets Keepalive restent certes attribués à chaque worker, mais leur répartition est plus homogène. Pour les backends dynamiques qui changent d'adresse via le DNS, je configure „résoudre“ » au niveau des lignes du serveur et définis un résolveur. Important : lorsque les adresses IP changent, le pool ne recycle pas immédiatement toutes les anciennes sockets ; je pense donc que keepalive_requests et des délais réalistes, afin que la rénovation porte rapidement ses fruits.
upstream backend_pool {
zone backend_zone 128k; # partage l'état du répartiteur de charge
least_conn; # répartition équitable pour les requêtes longues
server app-1.internal:8080 resolve;
server app-2.internal:8080 resolve;
keepalive 64;
keepalive_requests 1000;
keepalive_timeout 60s;
}
resolver 10.0.0.2 valid=30s;
resolver_timeout 5s;
proxy_next_upstream error timeout http_502 http_504;
proxy_next_upstream_tries 2; # : quelques tentatives ciblées
Pour les sessions liées à un nœud backend spécifique (par exemple, en mode « sticky state »), je combine la réutilisation avec ip_hash ou un mécanisme de session externe. Cela permet d'éviter que la mise en pool des connexions ne perturbe la cohérence des sessions.
TLS vers le backend : SNI, réutilisation des sessions et algorithmes de chiffrement
Plus l'utilisation de TLS est importante dans le chemin d'accès au backend, plus la fonctionnalité Keepalive est précieuse. J'active le SNI, je définis le nom attendu et je veille à la réutilisation de la session TLS. Cela réduit les coûts liés à la négociation de connexion et atténue les pics de latence. Je choisis les suites de chiffrement et les protocoles de manière restrictive, sans pour autant exclure les backends plus anciens. Lors de la vérification du certificat (facultative), la chaîne de confiance doit être complète, sinon les connexions sont sporadiquement interrompues.
upstream https_backend {
server backend.example.local:443;
keepalive 32;
}
server {
listen 443 ssl;
location / {
proxy_pass https://https_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_ssl_server_name on;
proxy_ssl_name backend.example.local;
proxy_ssl_session_reuse on;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
proxy_ssl_ciphers HIGH:!aNULL:!MD5;
# optional: proxy_ssl_verify on;
# optional: proxy_ssl_trusted_certificate /etc/nginx/ca.pem;
}
}
Lorsque je gère moi-même le backend, j’y active les tickets de session ou les caches, puis je vérifie à l’aide d’indicateurs si les taux de reprise augmentent. En combinaison avec Keepalive, j’obtiens ainsi des temps de connexion et de handshake constamment faibles.
Cas particuliers : gRPC, WebSockets et authentification liée à la connexion
À l'adresse suivante : gRPC NGINX fonctionne en amont via HTTP/2. Dans ce cas, un petit nombre de connexions persistantes avec de nombreux flux donne souvent les meilleurs résultats ; le pool reste petit, mais stable. Pour WebSockets Je définis des délais d'attente de lecture longs et je conserve la logique de gestion des en-têtes issue de la solution « map », afin d'éviter que les connexions de mise à niveau ne soient fermées par inadvertance. NTLM ou d'autres méthodes d'authentification liées à la connexion nécessitent le « connection pinning » ; je sépare ces chemins dans des emplacements distincts et j'y limite le pooling ou la réutilisation afin d'éviter que les handshakes de sécurité ne soient mélangés entre les clients.
Exemple gRPC #
location /grpc.Service/ {
grpc_pass grpc://backend_pool;
grpc_read_timeout 300s; Autoriser les flux longs #
}
Il est essentiel de définir une politique de connexion cohérente pour chaque chemin et de n'utiliser le « keepalive » à grande échelle que là où cela n'a pas d'importance d'un point de vue sémantique.
Mesurabilité dans la pratique : journaux d'accès avec chronométrages en amont
J'ajoute des métriques en amont au journal d'accès. Cela me permet de voir d'un seul coup d'œil si une réponse provient d'un socket mis en pool (temps de connexion très court) et à quelle fréquence des erreurs backend se produisent. De plus, j'enregistre le numéro de connexion et le nombre de requêtes effectuées via la connexion client en cours, afin d'établir des corrélations.
log_format upstream_timing '$remote_addr - $host "$request" '
'up=$upstream_addr '
'sc=$status usc=$upstream_status '
'cc=$connection cr=$connection_requests '
'tc=$upstream_connect_time '
'th=$upstream_header_time '
'tr=$upstream_response_time' ;
access_log /var/log/nginx/access_upstream.log upstream_timing;
De plus, j'utilise les points de terminaison d'état et les statistiques de sockets du système d'exploitation. Un état sain se caractérise par : un taux de connexion au backend en baisse, un `upstream_connect_time` plus court, des temps de réponse stables et très peu de réinitialisations de connexion. Tout écart indique presque toujours des délais d'expiration mal paramétrés ou des pools trop petits ou trop grands.
Stratégie de déploiement et optimisation à faible risque
Je procède par étapes : petits pas, mesure, ajustement. Je commence par activer le Keepalive de manière modérée, puis j'ajuste les délais d'expiration et le nombre de requêtes par connexion. J'applique les modifications en rafraîchissant la page, sans interrompre les connexions actives. Cela permet de limiter les risques et d'identifier clairement les effets.
# Valider les modifications et les charger sans interruption de service
nginx -t && nginx -s reload
Lorsque je gère plusieurs flux en amont, je les optimise les uns après les autres, en commençant par le chemin le plus critique. Je définis une fenêtre d'observation pour chaque étape afin de mettre clairement en évidence les tendances dans les métriques. Ce n'est qu'ensuite que j'ajuste les valeurs à la hausse ou à la baisse.
Résumé concis pour ton proxy inverse
J'utilise HTTP/1.1, je laisse l'en-tête « Connection » vide et je choisis la taille du pool en fonction du nombre de requêtes simultanées, et non du RPS ; cela contribue à la Performance. Grâce aux paramètres `keepalive_requests` et `keepalive_timeout`, je maintiens les connexions actives et j'évite les mauvaises surprises liées à des sockets obsolètes. La surveillance permet de vérifier si la valeur de `upstream_connect_time` tend vers zéro et si le débit de connexion vers le backend diminue. En cas d’erreurs, je vérifie d’abord la version du protocole, la transmission des en-têtes, les délais d’expiration et la taille du pool. Ainsi, votre proxy NGINX reste stable même sous une charge élevée réactif et prévisibles.


