Come Diagnosticare e Risolvere gli Errori Nginx 502 Bad Gateway

Risolvi gli errori Nginx 502 controllando i log degli errori, lo stato dell'upstream, i permessi dei socket, le impostazioni del proxy, i timeout e i firewall.

Come Diagnosticare e Risolvere gli Errori Nginx 502 Bad Gateway

Nginx è un potente e popolare server web e proxy inverso, spesso utilizzato per servire contenuti statici, bilanciare il traffico e inoltrare richieste a vari server applicativi upstream come PHP-FPM, Node.js, Python Gunicorn o Apache Tomcat. Quando Nginx incontra un problema di comunicazione con uno di questi server upstream, di solito risponde con un errore "502 Bad Gateway".

Inizia con il log degli errori di Nginx, poi verifica se il processo upstream è in esecuzione, raggiungibile e autorizzato a rispondere.

Comprendere l'Errore Nginx 502 Bad Gateway

Un errore 502 Bad Gateway indica che Nginx, agendo come proxy inverso, ha ricevuto una risposta non valida da un server upstream. Significa che Nginx si è connesso con successo a un server upstream ma ha ricevuto nessuna risposta, una risposta incompleta o una risposta che non poteva comprendere. Fondamentalmente, il problema non è con Nginx stesso, ma con il servizio con cui Nginx sta cercando di comunicare.

I server upstream comuni includono:

  • PHP-FPM: Per applicazioni PHP (es. WordPress, Laravel).
  • Gunicorn/uWSGI: Per applicazioni Python (es. Django, Flask).
  • Node.js: Per applicazioni JavaScript.
  • Apache Tomcat: Per applicazioni Java.
  • Altri server web: Come Apache HTTP Server che serve contenuti specifici.

L'errore 502 è un indicatore cruciale che il backend della tua applicazione non funziona correttamente o è inaccessibile a Nginx.

Diagnosi Passo dopo Passo

La chiave per risolvere un errore 502 è una diagnosi sistematica. Inizia con i colpevoli più probabili e procedi progressivamente.

1. Controlla Prima i Log degli Errori di Nginx

I log degli errori di Nginx sono la fonte primaria di informazioni. Spesso contengono dettagli specifici sul motivo per cui Nginx non è riuscito a comunicare con il server upstream.

  • Posizione: Tipicamente si trovano in /var/log/nginx/error.log.
  • Comando: Usa tail -f per monitorare i log in tempo reale mentre provi a riprodurre l'errore.
tail -f /var/log/nginx/error.log

Cosa cercare:

  • connect() failed (111: Connection refused): Indica che il server upstream non è in ascolto sull'indirizzo/porta specificati o che un firewall blocca la connessione.
  • upstream timed out: Il server upstream ha impiegato troppo tempo per rispondere.
  • upstream prematurely closed connection: Il server upstream ha chiuso la connessione prima di inviare una risposta completa.
  • no live upstreams while connecting to upstream: Nginx non è riuscito a trovare alcun server upstream disponibile configurato.

2. Verifica lo Stato del Server Upstream

Una volta ottenuti indizi dai log degli errori di Nginx, controlla lo stato del tuo server applicativo upstream.

  • Per PHP-FPM:

    sudo systemctl status php8.2-fpm
    
  • Per Node.js/Python/Altre App Personalizzate: Controlla se il processo è in esecuzione.

    ps aux | grep node
    ps aux | grep gunicorn
    

    Se usi un gestore di processi come PM2 (Node.js) o Supervisor (generale), controlla il suo stato.

    pm2 status
    sudo supervisorctl status
    

Se il servizio non è in esecuzione, prova ad avviarlo e controlla i suoi log per errori.

sudo systemctl start php8.2-fpm

3. Controlla la Connettività di Rete all'Upstream

Assicurati che Nginx possa raggiungere il server upstream sulla porta o sul percorso del socket configurato.

  • Per connessioni TCP/IP (es. 127.0.0.1:8000): Usa telnet o nc (netcat) per testare la connettività della porta dal server Nginx.

    telnet 127.0.0.1 8000
    nc -vz 127.0.0.1 8000
    

    Una connessione riuscita dovrebbe mostrare Connected to 127.0.0.1. o succeeded!. Se si blocca o mostra Connection refused, il servizio upstream non è in ascolto o un firewall lo blocca.

  • Per socket Unix (es. unix:/run/php/phpX.X-fpm.sock): Verifica che il file del socket esista e abbia i permessi corretti.

    ls -l /run/php/phpX.X-fpm.sock
    

    Nginx dovrebbe avere permessi di lettura/scrittura su questo file socket. L'utente Nginx (es. www-data) deve far parte del gruppo che possiede il socket (es. www-data o php-fpm).

Cause Comuni e Soluzioni

Basandoti sui tuoi passaggi diagnostici, ecco le cause più frequenti degli errori 502 e come risolverli.

1. Server Upstream Non in Esecuzione o Bloccato

Causa: L'applicazione a cui Nginx sta cercando di fare da proxy (es. PHP-FPM, Gunicorn, app Node.js) non è in esecuzione o si è bloccata.

Soluzione: Avvia o riavvia il servizio upstream.

# Esempio per PHP-FPM
sudo systemctl start php8.2-fpm
# Se è già in esecuzione e sospetti un crash, riavvialo:
sudo systemctl restart php8.2-fpm

# Per applicazioni personalizzate, usa i loro comandi specifici di avvio/riavvio

Consiglio: Assicurati che i tuoi servizi upstream siano configurati per avviarsi automaticamente all'avvio del sistema. Per i servizi systemd, usa systemctl enable phpX.X-fpm.

2. Sovraccarico del Server Upstream / Esaurimento delle Risorse

Causa: Il server upstream è sovraccarico, sta esaurendo la memoria, la CPU o raggiungendo i limiti dei processi, causando la cessazione delle risposte o il rifiuto di nuove connessioni.

Sintomi: I log degli errori di Nginx potrebbero mostrare connection refused o upstream timed out in modo intermittente, specialmente sotto carico. Gli strumenti di monitoraggio del sistema (top, htop, free -h) mostrano un uso elevato delle risorse.

Soluzioni:

  • Per PHP-FPM: Regola le impostazioni del pool PHP-FPM nel suo file di configurazione (es. /etc/php/X.X/fpm/pool.d/www.conf).

    • pm.max_children: Il numero massimo di figli che possono essere vivi contemporaneamente.
    • pm.start_servers: Il numero di figli creati all'avvio.
    • pm.min_spare_servers, pm.max_spare_servers: Controllano quanti figli inattivi vengono mantenuti.
    ; Esempio per la gestione dinamica dei processi
    pm = dynamic
    pm.max_children = 50
    pm.start_servers = 10
    pm.min_spare_servers = 5
    pm.max_spare_servers = 20
    
    • Aumenta memory_limit in php.ini se gli script esauriscono la memoria.
  • Per altre applicazioni: Aumenta il numero di processi worker, thread o alloca più memoria se possibile. Monitora le metriche specifiche della tua applicazione.

  • Timeout di Nginx: Aumenta le direttive proxy_connect_timeout, proxy_send_timeout e proxy_read_timeout di Nginx nella tua configurazione Nginx, ma comprendi che questo ritarda semplicemente l'errore se il backend è veramente in difficoltà.

    http {
        ...
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
        ...
    }
    

3. Configurazione Upstream Errata in Nginx

Causa: Nginx è configurato per connettersi all'indirizzo IP, alla porta o al percorso del socket Unix sbagliato per il server upstream.

Sintomi: I log degli errori di Nginx mostrano connect() failed (111: Connection refused) immediatamente dopo una richiesta.

Soluzione: Rivedi attentamente la configurazione del blocco server di Nginx (/etc/nginx/sites-available/your_site.conf).

  • Per upstream HTTP/HTTPS:

    location /app {
        proxy_pass http://127.0.0.1:8000; # Assicurati che IP e porta siano corretti
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    
  • Per PHP-FPM tramite socket Unix:

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/phpX.X-fpm.sock; # Verifica che questo percorso corrisponda esattamente alla configurazione PHP-FPM
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
    
  • Per PHP-FPM tramite TCP/IP:

    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000; # Verifica IP e porta
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
    

Dopo aver apportato modifiche, testa sempre la configurazione di Nginx e ricarica/riavvia Nginx:

nginx -t
systemctl reload nginx # O riavvia se -t indica una necessità

4. Superamento del request_terminate_timeout di PHP-FPM

Causa: Uno script PHP impiega più tempo per essere eseguito rispetto all'impostazione request_terminate_timeout in PHP-FPM. Nginx attende la risposta, ma PHP-FPM termina lo script, causando la ricezione di una risposta incompleta da parte di Nginx.

Sintomi: I log degli errori di Nginx potrebbero mostrare upstream timed out o script timed out. I log di PHP-FPM potrebbero mostrare child XX exited on signal 9 (SIGKILL).

Soluzione:

  • Aumenta request_terminate_timeout: Nella configurazione del pool PHP-FPM (www.conf), trova e regola questa direttiva. Impostarla a 0 disabilita il timeout, ma generalmente non è raccomandato poiché script a lunga esecuzione possono bloccare le risorse.

    request_terminate_timeout = 300 # Aumenta a 5 minuti (300 secondi)
    
  • Aumenta fastcgi_read_timeout in Nginx: Questo timeout di Nginx dovrebbe essere uguale o maggiore di request_terminate_timeout.

    location ~ \.php$ {
        ...
        fastcgi_read_timeout 300s; # Deve essere >= request_terminate_timeout di PHP-FPM
        ...
    }
    

Avvertenza: Sebbene aumentare i timeout possa risolvere l'errore 502, potrebbe mascherare problemi di performance sottostanti. La migliore soluzione a lungo termine è ottimizzare lo script PHP lento.

5. Problemi di Firewall

Causa: Un firewall (sul server Nginx o sul server upstream se sono separati) sta bloccando le connessioni alla porta o al socket upstream.

Soluzione:

  • Controlla lo stato del firewall:

    sudo ufw status # Per UFW (Ubuntu/Debian)
    sudo firewall-cmd --list-all # Per firewalld (CentOS/RHEL)
    sudo iptables -L # Per iptables
    
  • Apri le porte necessarie: Assicurati che la porta che Nginx usa per connettersi all'upstream (es. 9000 per PHP-FPM tramite TCP/IP) sia aperta.

    sudo ufw allow from 127.0.0.1 to any port 9000 # Consenti a localhost di connettersi alla porta 9000
    sudo firewall-cmd --permanent --add-port=9000/tcp # Per firewalld
    sudo firewall-cmd --reload
    
  • Disabilita temporaneamente il firewall per scopi di test solo in un ambiente controllato, poi riattivalo e configuralo correttamente.

6. Interferenza di SELinux o AppArmor

Causa: Miglioramenti della sicurezza come SELinux (su RHEL/CentOS) o AppArmor (su Ubuntu/Debian) potrebbero impedire a Nginx di accedere al socket upstream o di effettuare connessioni di rete, anche se i permessi dei file e i firewall sono configurati correttamente.

Sintomi: I log potrebbero mostrare permission denied o messaggi simili, specialmente in /var/log/audit/audit.log (per SELinux).

Soluzione:

  • Controlla audit.log:

    sudo grep nginx /var/log/audit/audit.log
    
  • Imposta temporaneamente SELinux in modalità permissiva: sudo setenforce 0. Se l'errore si risolve, SELinux è il colpevole. Dovrai quindi generare e applicare le politiche SELinux appropriate (es. audit2allow). Ricorda di riportarlo in modalità enforcing (sudo setenforce 1).

  • Controlla lo stato di AppArmor: sudo aa-status. Se AppArmor è attivo, potresti dover regolare il profilo di Nginx.

7. Corpi di Richiesta/Risposta Grandi (Proxy Buffering)

Causa: Le impostazioni predefinite del proxy buffering di Nginx potrebbero essere troppo piccole per corpi di richiesta o risposta molto grandi, portando a una chiusura prematura della connessione.

Sintomi: I log degli errori di Nginx potrebbero mostrare upstream prematurely closed connection while reading response header from upstream o upstream prematurely closed connection while reading response body from upstream.

Soluzione: Regola le direttive del proxy buffering di Nginx nel tuo blocco http, server o location.

http {
    ...
    proxy_buffer_size   128k; # Dimensione del buffer per la prima parte della risposta
    proxy_buffers   4 256k; # Numero e dimensione dei buffer per il resto della risposta
    proxy_busy_buffers_size   256k; # Dimensione massima dei buffer occupati
    proxy_temp_file_write_size 256k; # Dimensione per la scrittura su file temporanei se il buffering va in overflow
    ...
}

Nota: Queste impostazioni consumano più memoria. Regolale con cautela in base alle risorse del tuo server e alla dimensione tipica delle risposte della tua applicazione.

Suggerimenti Generali per la Risoluzione dei Problemi

  • Rivedi tutti i log pertinenti: Oltre ai log degli errori di Nginx, controlla anche i log di accesso di Nginx, i log dell'applicazione upstream (PHP-FPM, Gunicorn, log dell'app Node.js) e i log di sistema (/var/log/syslog, dmesg).
  • Riavvia Nginx: Dopo qualsiasi modifica alla configurazione, riavvia sempre Nginx per assicurarti che abbiano effetto: systemctl restart nginx.
  • Testa la Configurazione di Nginx: Prima di riavviare, convalida la sintassi della configurazione di Nginx: nginx -t.
  • Isola il Problema: Prova a bypassare Nginx e ad accedere direttamente all'applicazione upstream. Ad esempio, se la tua app Node.js è su localhost:3000, usa curl http://localhost:3000 dalla riga di comando del server. Se anche questo fallisce, il problema è sicuramente con la tua applicazione, non con Nginx.
  • Controlla lo Spazio su Disco: Un disco pieno può impedire alle applicazioni di scrivere file temporanei o log, portando a crash o guasti. Usa df -h per controllare l'utilizzo del disco.

Conclusione

Inizia con /var/log/nginx/error.log, poi verifica che l'upstream sia in esecuzione e raggiungibile dall'host Nginx. Una volta che sai se il guasto è connessione rifiutata, timeout, permesso negato o chiusura prematura, la soluzione è di solito nel servizio upstream, nei permessi del socket, nelle impostazioni di timeout o nella regola del firewall.