Résolution des problèmes de délai d'attente 504 Gateway Timeout et de délai d'attente client dans Nginx

Maîtrisez les délais d'attente Nginx, y compris le redouté 504 Gateway Timeout, en apprenant à ajuster les directives proxy critiques. Ce guide détaille comment augmenter `proxy_read_timeout`, optimiser la mise en mémoire tampon et utiliser les journaux d'erreurs pour diagnostiquer les échecs de communication entre Nginx et les serveurs en amont pour une gestion robuste des connexions.

Résolution des problèmes de délai d'attente 504 Gateway Timeout et de délai d'attente client dans Nginx

Un 504 Gateway Timeout dans Nginx signifie que Nginx agissait en tant que proxy ou passerelle et n'a pas reçu de réponse du service en amont à temps. L'amont peut être une application Node.js, Gunicorn, PHP-FPM, un autre service Nginx, une API interne ou un équilibreur de charge.

La correction tentante est d'augmenter chaque délai d'attente à cinq minutes et de passer à autre chose. Parfois, un délai d'attente plus long est la bonne décision à court terme, en particulier pour les rapports, les importations ou les tâches réservées aux administrateurs. Mais si une demande d'utilisateur normal nécessite plus de temps que ce que Nginx autorise actuellement, vous devez également vous demander pourquoi le backend est lent, si la demande doit être asynchrone et si un autre proxy dans le chemin a un délai d'attente plus court de toute façon.


Comprendre l'erreur 504 Gateway Timeout

Une erreur 504 Gateway Timeout se produit lorsque Nginx, agissant en tant que proxy inverse ou passerelle, ne reçoit pas de réponse en temps opportun du serveur en amont auquel il transmet les demandes. En termes simples : Nginx a demandé une réponse au backend, a attendu la durée configurée et a abandonné car aucune réponse n'est arrivée.

Ceci est différent d'un 502 Bad Gateway, où Nginx a reçu une réponse en amont invalide ou prématurément fermée, et d'un 503 Service Unavailable, qui signifie souvent qu'un service est intentionnellement indisponible ou surchargé. La distinction est importante car un 504 vous oriente vers l'attente, les délais et la latence en amont.

Directives clés contrôlant les délais d'attente en amont

Lors du proxy de demandes, Nginx utilise plusieurs directives critiques, principalement situées dans les blocs http, server ou location, ou spécifiquement dans un bloc upstream. Ajuster ces valeurs est la méthode principale pour résoudre les erreurs 504.

1. proxy_connect_timeout

Ceci définit le délai d'attente pour établir une connexion avec le serveur en amont. Si Nginx ne peut pas se connecter dans ce délai, il renvoie une erreur de délai d'attente.

Par défaut : 60 secondes

proxy_connect_timeout 60s;

2. proxy_send_timeout

Ceci définit le délai d'attente entre deux opérations d'écriture successives vers le serveur en amont. Ceci est pertinent lors de l'envoi d'un corps de requête volumineux.

Par défaut : 60 secondes

proxy_send_timeout 60s;

3. proxy_read_timeout (La correction la plus courante pour les 504)

Ceci définit le délai d'attente pour attendre une réponse du serveur en amont après l'envoi des en-têtes de la requête. Si l'application backend prend trop de temps pour traiter la demande et générer un corps de réponse, c'est la directive qui doit être augmentée.

Par défaut : 60 secondes

# Exemple : Augmentation du délai de lecture à 120 secondes pour une API lente
proxy_read_timeout 120s;

Si votre application dépasse fréquemment la valeur par défaut, augmentez cette valeur avec prudence et continuez à enquêter. Une valeur très élevée peut maintenir les connexions client ouvertes alors que le backend est déjà défaillant.


Traitement des délais d'attente côté client

Les délais d'attente côté client sont une défaillance différente. Le navigateur, l'application mobile, l'équilibreur de charge, le CDN ou le service appelant abandonne avant que Nginx ne termine la réponse. Dans ce cas, l'utilisateur peut voir une erreur de navigateur ou une erreur de passerelle provenant d'une couche devant Nginx, tandis que Nginx peut enregistrer une connexion fermée plutôt qu'un 504 propre.

Si vous rencontrez des délais d'attente client avant que Nginx n'enregistre un 504, vous devez examiner la connexion entre le client et Nginx.

1. Keepalive côté client

Si le client ferme la connexion prématurément, Nginx peut recevoir une erreur ou le client peut simplement expirer en attendant les données.

Si le client est un autre proxy ou équilibreur de charge, vérifiez ses paramètres de délai d'attente par rapport à Nginx et au backend. Le délai d'attente le plus court dans la chaîne l'emporte généralement. Un schéma courant est : CDN attend 100 secondes, équilibreur de charge attend 60 secondes, Nginx attend 180 secondes, backend prend 120 secondes. Les utilisateurs échouent toujours à 60 secondes car l'équilibreur de charge abandonne en premier.

2. send_timeout de Nginx

Cette directive contrôle combien de temps Nginx attendra que le client accuse réception ou reçoive des données (le temps entre deux opérations d'écriture successives vers le client).

Par défaut : 60 secondes

# Définissez ceci si les clients expirent pendant que Nginx envoie la réponse
send_timeout 120s;

Optimisation de la mise en mémoire tampon pour les réponses volumineuses

Parfois, le backend commence à répondre, mais la livraison est toujours lente car la réponse est énorme, le client est lent ou Nginx doit mettre en mémoire tampon plus que prévu. Ceci est courant avec les exportations CSV générées, les téléchargements de médias acheminés via une application ou les API qui renvoient de très gros payloads JSON.

Nginx utilise des tampons pour stocker temporairement les données reçues de l'amont avant de les envoyer au client. Si la réponse est très volumineuse, ces tampons peuvent être dépassés, entraînant une gestion complexe ou une latence perçue.

Directives clés de mise en mémoire tampon

Celles-ci sont généralement définies dans le bloc location ou server :

Directive Objectif
proxy_buffers Définit le nombre et la taille des tampons utilisés pour lire la réponse de l'amont. Format : nombre taille;
proxy_buffer_size Définit la taille du premier tampon, utilisé pour lire l'en-tête de la réponse.
proxy_max_temp_file_size Si la réponse dépasse les tampons disponibles, Nginx écrit dans des fichiers temporaires. Ceci définit la taille maximale de ces fichiers temporaires.

Exemple de configuration pour un volume élevé/réponses volumineuses :

location /api/heavy_report {
    proxy_pass http://backend_app;

    # Augmenter le délai de lecture
    proxy_read_timeout 180s;

    # Ajuster la mise en mémoire tampon pour les corps de réponse potentiellement volumineux
    # Utiliser 8 tampons, chacun jusqu'à 1 Mo (1024k)
    proxy_buffers 8 1024k;
    proxy_buffer_size 256k;

    # Autoriser les fichiers temporaires jusqu'à 500 Mo si les tampons débordent
    proxy_max_temp_file_size 500m;
}

Si la réponse de votre backend est vraiment énorme, envisagez de servir un fichier généré à partir d'un stockage d'objets ou d'un stockage statique au lieu de maintenir la demande ouverte via l'application. Pour les exportations, un schéma courant est : mettre en file d'attente la tâche, générer le fichier, puis laisser l'utilisateur le télécharger à partir d'une URL statique lorsqu'il est prêt.


Étapes de dépannage et analyse des journaux

La résolution des délais d'attente nécessite de localiser l'endroit où le blocage s'est produit : Client -> Nginx, ou Nginx -> Backend.

Étape 1 : Vérifier les journaux d'erreurs Nginx

Le journal des erreurs Nginx est votre source définitive pour déterminer si Nginx a expiré en attendant le backend.

Recherchez des entrées contenant des expressions comme :

  • upstream timed out (110: Connection timed out)
  • upstream prematurely closed connection while reading response header from upstream

Si vous voyez ces messages, le problème vient de proxy_read_timeout ou du temps de traitement du backend.

Recherchez également client prematurely closed connection. Cela signifie généralement que le client ou un proxy devant Nginx a abandonné en premier. Dans ce cas, augmenter proxy_read_timeout seul n'aidera pas l'utilisateur.

Étape 2 : Vérifier les journaux de l'application backend

Si Nginx expire (les journaux indiquent 504), vérifiez immédiatement les journaux du service en amont (par exemple, les journaux PHP-FPM, les journaux Gunicorn, les journaux du serveur d'applications Java). Vous devez confirmer si la demande a même atteint le backend et combien de temps elle a pris pour se terminer.

  • Si les journaux backend montrent que la demande a pris plus de temps que votre proxy_read_timeout configuré, augmentez le délai d'attente Nginx.
  • Si les journaux backend montrent que la demande s'est terminée rapidement, le problème peut être la latence du réseau entre Nginx et le backend, ou un délai d'attente client mal configuré face à Nginx.

Étape 3 : Utiliser l'en-tête X-Upstream-Response-Time (Facultatif)

Pour un diagnostic détaillé, vous pouvez enregistrer le temps exact pris par l'amont pour répondre en utilisant la variable $upstream_response_time dans votre format de journal d'accès. Cela aide à confirmer les performances réelles du backend.

Dans votre nginx.conf :

log_format proxy_detailed '$remote_addr - $remote_user [$time_local] "$request" '
                        '$status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" $request_time $upstream_response_time';

access_log /var/log/nginx/access.log proxy_detailed;

En analysant $upstream_response_time, vous pouvez voir la durée précise pendant laquelle Nginx a attendu, indépendamment des propres paramètres de délai d'attente de Nginx.

Pour un test ponctuel rapide, appelez l'amont directement depuis l'hôte Nginx :

time curl -sS -o /dev/null -w 'status=%{http_code} total=%{time_total}\n' http://127.0.0.1:3000/slow-route

Si l'appel amont direct est déjà lent, Nginx ne fait que signaler le problème. Si l'appel direct est rapide mais que la demande proxy expire, inspectez la configuration du proxy, la résolution DNS, la mise en réseau des conteneurs, le TLS entre les services internes ou un autre saut entre Nginx et l'application.


Appliquer le plus petit changement utile

Une correction de production raisonnable ressemble souvent à ceci :

location /api/reports/ {
    proxy_pass http://backend_app;
    proxy_connect_timeout 10s;
    proxy_send_timeout 60s;
    proxy_read_timeout 180s;
}

Cela augmente le délai de lecture uniquement pour le point de terminaison de rapport lent. Cela ne fait pas attendre trois minutes chaque demande sur le site. Pour une route de connexion, une route de paiement, une vérification de santé ou un point de terminaison d'API publique, un long délai d'attente peut rendre les échecs plus douloureux car les clients attendent plus longtemps une demande qui a peu de chances de récupérer.

Pour PHP-FPM, l'équivalent peut impliquer des directives FastCGI :

location ~ \.php$ {
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    fastcgi_read_timeout 120s;
    include fastcgi_params;
}

N'oubliez pas que PHP, Python, Node.js, les serveurs d'applications, les files d'attente, les bases de données, les CDN et les équilibreurs de charge peuvent tous avoir leurs propres paramètres de délai d'attente. Nginx ne peut pas faire en sorte qu'un backend continue de fonctionner après que le délai d'attente de son propre worker a tué la demande.

Après avoir effectué des modifications de configuration (par exemple, augmentation des délais d'attente ou ajustement des tailles de tampon), testez toujours la syntaxe de configuration et rechargez Nginx :

sudo nginx -t
sudo systemctl reload nginx

Ensuite, surveillez les journaux Nginx et amont tout en répétant la même demande :

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

La meilleure correction de délai d'attente vous laisse avec une raison claire pour le changement : cette route prend légitimement jusqu'à deux minutes, cet amont a maintenant des limites correspondantes, et les demandes lentes sont visibles dans les journaux ou les métriques. Tout le reste est un correctif temporaire, et les correctifs temporaires doivent être étiquetés comme tels dans votre révision de configuration ou vos notes d'incident.