Risoluzione dei problemi di timeout 504 Gateway e client in Nginx
Padroneggia i timeout di Nginx, incluso il temuto 504 Gateway Timeout, imparando a regolare le direttive proxy critiche. Questa guida spiega come aumentare `proxy_read_timeout`, ottimizzare il buffering e utilizzare i log degli errori per diagnosticare i fallimenti di comunicazione tra Nginx e i server upstream per una gestione robusta delle connessioni.
Risoluzione dei problemi di timeout 504 Gateway e client in Nginx
Un errore 504 Gateway Timeout in Nginx significa che Nginx stava agendo come proxy o gateway e non ha ricevuto una risposta dal servizio upstream in tempo. L'upstream potrebbe essere un'app Node.js, Gunicorn, PHP-FPM, un altro servizio Nginx, un'API interna o un bilanciatore di carico.
La soluzione allettante è aumentare ogni timeout a cinque minuti e proseguire. A volte un timeout più lungo è la mossa giusta a breve termine, specialmente per report, importazioni o lavori solo per amministratori. Ma se una normale richiesta utente richiede più tempo di quanto Nginx attualmente consenta, dovresti anche chiederti perché il backend è lento, se la richiesta dovrebbe essere asincrona e se un altro proxy nel percorso ha comunque un timeout più breve.
Comprendere l'errore 504 Gateway Timeout
Un errore 504 Gateway Timeout si verifica quando Nginx, agendo come proxy inverso o gateway, non riceve una risposta tempestiva dal server upstream a cui sta inoltrando le richieste. In termini semplici: Nginx ha chiesto una risposta al backend, ha aspettato per il tempo configurato e ha rinunciato perché nessuna risposta è arrivata.
Questo è diverso da un 502 Bad Gateway, dove Nginx ha ricevuto una risposta upstream non valida o chiusa prematuramente, e da un 503 Service Unavailable, che spesso significa che un servizio è intenzionalmente non disponibile o sovraccarico. La distinzione è importante perché un 504 ti indirizza verso l'attesa, i tempi e la latenza dell'upstream.
Direttive chiave che controllano i timeout upstream
Quando si inoltrano richieste, Nginx utilizza diverse direttive critiche, situate principalmente all'interno dei blocchi http, server o location, o specificamente all'interno di un blocco upstream. Regolare questi valori è il metodo principale per risolvere gli errori 504.
1. proxy_connect_timeout
Imposta il timeout per stabilire una connessione con il server upstream. Se Nginx non riesce a connettersi entro questo periodo, restituisce un errore di timeout.
Predefinito: 60 secondi
proxy_connect_timeout 60s;
2. proxy_send_timeout
Imposta il timeout per il tempo tra due operazioni di scrittura successive al server upstream. Questo è rilevante quando si invia un corpo richiesta di grandi dimensioni.
Predefinito: 60 secondi
proxy_send_timeout 60s;
3. proxy_read_timeout (La correzione più comune per i 504)
Imposta il timeout per attendere una risposta dal server upstream dopo che le intestazioni della richiesta sono state inviate. Se l'applicazione backend impiega troppo tempo per elaborare la richiesta e generare un corpo di risposta, questa è la direttiva che deve essere aumentata.
Predefinito: 60 secondi
# Esempio: aumento del timeout di lettura a 120 secondi per un'API lenta
proxy_read_timeout 120s;
Se la tua applicazione supera frequentemente il valore predefinito, aumenta questo valore con cautela e continua a indagare. Un valore molto alto può mantenere aperte le connessioni client mentre il backend è già in uno stato non salutare.
Affrontare i timeout lato client
I timeout lato client sono un fallimento diverso. Il browser, l'app mobile, il bilanciatore di carico, la CDN o il servizio chiamante rinunciano prima che Nginx completi la risposta. In tal caso, l'utente potrebbe vedere un errore del browser o un errore di gateway da un livello davanti a Nginx, mentre Nginx potrebbe registrare una connessione chiusa piuttosto che un 504 pulito.
Se stai riscontrando timeout client prima che Nginx registri un 504, devi esaminare la connessione tra il client e Nginx.
1. Keepalive lato client
Se il client chiude la connessione prematuramente, Nginx potrebbe ricevere un errore o il client potrebbe semplicemente scadere in attesa dei dati.
Se il client è un altro proxy o bilanciatore di carico, controlla le sue impostazioni di timeout rispetto a Nginx e al backend. Il timeout più breve nella catena di solito vince. Uno schema comune è: CDN aspetta 100 secondi, bilanciatore di carico aspetta 60 secondi, Nginx aspetta 180 secondi, backend impiega 120 secondi. Gli utenti falliscono comunque a 60 secondi perché il bilanciatore di carico rinuncia per primo.
2. send_timeout di Nginx
Questa direttiva controlla per quanto tempo Nginx aspetterà che il client riconosca o riceva dati (il tempo tra due operazioni di scrittura successive al client).
Predefinito: 60 secondi
# Imposta questo se i client vanno in timeout mentre Nginx sta inviando la risposta
send_timeout 120s;
Ottimizzazione del buffering per risposte di grandi dimensioni
A volte il backend inizia a rispondere, ma la consegna è ancora lenta perché la risposta è enorme, il client è lento o Nginx deve bufferizzare più del previsto. Questo è comune con esportazioni CSV generate, download multimediali instradati tramite un'app o API che restituiscono payload JSON molto grandi.
Nginx utilizza buffer per trattenere temporaneamente i dati ricevuti dall'upstream prima di inviarli al client. Se la risposta è molto grande, questi buffer possono essere superati, portando a una gestione complessa o a una latenza percepita.
Direttive chiave per il buffering
Queste sono solitamente impostate all'interno del blocco location o server:
| Direttiva | Scopo |
|---|---|
proxy_buffers |
Imposta il numero e la dimensione dei buffer utilizzati per leggere la risposta dall'upstream. Formato: numero dimensione; |
proxy_buffer_size |
Imposta la dimensione del primo buffer, utilizzato per leggere l'intestazione della risposta. |
proxy_max_temp_file_size |
Se la risposta supera i buffer disponibili, Nginx scrive su file temporanei. Questo imposta la dimensione massima per questi file temporanei. |
Esempio di configurazione per volumi elevati/risposte di grandi dimensioni:
location /api/heavy_report {
proxy_pass http://backend_app;
# Aumenta il timeout di lettura
proxy_read_timeout 180s;
# Ottimizza il buffering per corpi di risposta potenzialmente grandi
# Usa 8 buffer, ciascuno fino a 1MB (1024k)
proxy_buffers 8 1024k;
proxy_buffer_size 256k;
# Consenti file temporanei fino a 500MB se i buffer traboccano
proxy_max_temp_file_size 500m;
}
Se la risposta del tuo backend è veramente enorme, considera di servire un file generato dall'archiviazione di oggetti o dall'archiviazione statica invece di mantenere la richiesta aperta attraverso l'app. Per le esportazioni, uno schema comune è: accoda il lavoro, genera il file, quindi lascia che l'utente lo scarichi da un URL statico quando è pronto.
Passaggi per la risoluzione dei problemi e analisi dei log
Risolvere i timeout richiede di individuare dove si è verificato il blocco: Client -> Nginx, o Nginx -> Backend.
Passaggio 1: Controlla i log degli errori di Nginx
Il log degli errori di Nginx è la tua fonte definitiva per determinare se Nginx è andato in timeout in attesa del backend.
Cerca voci contenenti frasi come:
upstream timed out (110: Connection timed out)upstream prematurely closed connection while reading response header from upstream
Se vedi queste, il problema risiede in proxy_read_timeout o nel tempo di elaborazione del backend.
Cerca anche client prematurely closed connection. Questo di solito significa che il client o un proxy davanti a Nginx ha rinunciato per primo. In tal caso, aumentare solo proxy_read_timeout non aiuterà l'utente.
Passaggio 2: Controlla i log dell'applicazione backend
Se Nginx va in timeout (i log indicano 504), controlla immediatamente i log del servizio upstream (ad esempio, log PHP-FPM, log Gunicorn, log del server applicativo Java). Devi confermare se la richiesta è arrivata al backend e quanto tempo ha impiegato per essere completata.
- Se i log del backend mostrano che la richiesta ha impiegato più tempo del tuo
proxy_read_timeoutconfigurato, aumenta il timeout di Nginx. - Se i log del backend mostrano che la richiesta è stata completata rapidamente, il problema potrebbe essere la latenza di rete tra Nginx e il backend o un timeout client configurato male di fronte a Nginx.
Passaggio 3: Usa l'intestazione X-Upstream-Response-Time (Opzionale)
Per diagnostiche dettagliate, puoi registrare il tempo esatto impiegato dall'upstream per rispondere utilizzando la variabile $upstream_response_time nel formato del tuo log di accesso. Questo aiuta a confermare le prestazioni effettive del backend.
Nel tuo 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;
Analizzando $upstream_response_time, puoi vedere la durata precisa che Nginx ha aspettato, indipendentemente dalle impostazioni di timeout di Nginx stesso.
Per un test rapido una tantum, chiama l'upstream direttamente dall'host Nginx:
time curl -sS -o /dev/null -w 'status=%{http_code} total=%{time_total}\n' http://127.0.0.1:3000/slow-route
Se la chiamata upstream diretta è già lenta, Nginx sta solo segnalando il problema. Se la chiamata diretta è veloce ma la richiesta inoltrata va in timeout, ispeziona la configurazione del proxy, la risoluzione DNS, la rete dei container, TLS tra servizi interni o un altro hop tra Nginx e l'app.
Applica la modifica più piccola utile
Una correzione di produzione ragionevole spesso si presenta così:
location /api/reports/ {
proxy_pass http://backend_app;
proxy_connect_timeout 10s;
proxy_send_timeout 60s;
proxy_read_timeout 180s;
}
Ciò aumenta il timeout di lettura solo per l'endpoint del report lento. Non fa aspettare tre minuti ogni richiesta sul sito. Per una route di login, una route di checkout, un health check o un endpoint API pubblico, un timeout lungo può rendere i fallimenti più dolorosi perché i client aspettano più a lungo per una richiesta che difficilmente si riprenderà.
Per PHP-FPM, l'equivalente può coinvolgere direttive FastCGI:
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_read_timeout 120s;
include fastcgi_params;
}
Ricorda che PHP, Python, Node.js, server applicativi, code, database, CDN e bilanciatori di carico possono tutti avere le proprie impostazioni di timeout. Nginx non può far continuare a funzionare un backend dopo che il timeout del worker del backend stesso ha ucciso la richiesta.
Dopo aver apportato qualsiasi modifica alla configurazione (ad esempio, aumentando i timeout o regolando le dimensioni del buffer), testa sempre la sintassi della configurazione e ricarica Nginx:
sudo nginx -t
sudo systemctl reload nginx
Quindi osserva sia i log di Nginx che quelli upstream mentre ripeti la stessa richiesta:
sudo tail -f /var/log/nginx/error.log
La migliore correzione del timeout ti lascia con una chiara ragione per la modifica: questa route richiede legittimamente fino a due minuti, questo upstream ora ha limiti corrispondenti e le richieste lente sono visibili nei log o nelle metriche. Qualcosa di meno è una patch temporanea, e le patch temporanee dovrebbero essere etichettate come tali nella tua revisione della configurazione o nelle note sugli incidenti.