WEB Server

Nginx 503 Service Unavailable

Il 503 Service Unavailable è il terzo errore della famiglia 5xx che completa la serie dedicata agli errori più comuni su Nginx, dopo il 502 Bad Gateway e il 504 Gateway Timeout. A differenza dei due precedenti dove il problema è quasi sempre nella comunicazione tra nginx e il backend, l’errore 503 ha una caratteristica distintiva: può essere intenzionale. Il server sa che non può gestire la richiesta e lo comunica esplicitamente al client, indicando che la situazione è temporanea.

Questo lo rende un errore con una doppia natura: da un lato un problema da risolvere (server sovraccarico, backend irraggiungibile), dall’altro uno strumento che l’amministratore può usare durante le finestre di manutenzione.

Non si tratta di un errore permanente, il protocollo HTTP prevede che il server possa includere nella risposta un header Retry-After per indicare al client quando riprovare.

Come visto per l’errore 502 e 504, il punto di partenza è sempre la lettura dei log

# access.log - dove e quando
grep " 503 " /var/log/nginx/access.log | tail -20

Se gli errori 503 compaiono su tutti gli URL contemporaneamente, il problema è sistemico (PHP-FPM saturo, backend down, rate limiting globale). Se compaiono solo su URL specifici, il problema è localizzato a quella risorsa o a quella regola di configurazione.

# error.log - motivo dell'errore
grep -E "503|no live upstreams|limiting requests" /var/log/nginx/error.log | tail -20

I messaggi tipici associati a un errore 503 possono essere

no live upstreams while connecting to upstream
limiting requests, excess: 10.456 by zone "one"
connect() to unix:/run/php/php8.2-fpm.sock failed (11: Resource temporarily unavailable)

PHP-FPM saturo

È la causa più comune di un errore 503 su ambienti PHP. Quando tutti i processi worker di PHP-FPM sono impegnati e la coda delle richieste in attesa è piena, PHP-FPM rifiuta le nuove connessioni restituendo a nginx un errore che si traduce in un 503.

# Messaggio nel log
connect() to unix:/run/php/php8.2-fpm.sock failed (11: Resource temporarily unavailable)

La differenza rispetto all’errore 502 è sottile: nel 502 PHP-FPM non è in esecuzione, nel 503 è in esecuzione ma ha esaurito la capacità di accettare nuove richieste. Possiamo verificarlo con i seguenti comandi

# Controllare il numero di processi PHP-FPM attivi
ps aux | grep php-fpm | wc -l

# Verificare se il pool ha raggiunto il limite
grep "max_children" /var/log/php8.2-fpm.log | tail -5

# Se nel log compare un errore simile il pool è saturo
WARNING: [pool www] server reached pm.max_children setting (10), consider raising it

A questo punto è necessario aumentare il valore di pm.max_children nel file di configurazione /etc/php/8.2/fpm/pool.d/www.conf

pm = dynamic
pm.max_children      = 30    # aumentare in base alla RAM disponibile
pm.start_servers     = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 15
pm.process_idle_timeout = 10s
pm.max_requests      = 500   # riavvia i worker dopo N richieste (previene memory leak)

La modifica della configurazione richiede il riavvio del servizio per applicare le modifiche

systemctl restart php8.2-fpm

Rate limiting attivo

Nginx include un modulo nativo di rate limiting (ngx_http_limit_req_module) che permette di limitare il numero di richieste per IP o per zona. Quando una richiesta supera il limite configurato, nginx restituisce un errore 503 (o 429, a seconda della configurazione).

# Messaggio nel log
limiting requests, excess: 10.456 by zone "one", client: 192.168.1.100

# Verifica se il rate limiting è configurato
grep -r "limit_req" /etc/nginx/

# Esempio di configurazione che genera un errore 503
http {
    limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;

    server {
        location / {
            limit_req zone=one;   # senza burst → 503 immediato se si supera 10r/s
        }
    }
}

La soluzione è quella di aggiungere burst nella direttiva per tollerare picchi di traffico temporanei senza generare un errore 503

location / {
    limit_req zone=one burst=20 nodelay;
    # burst=20: tollera fino a 20 richieste in eccesso
    # nodelay: le richieste nel burst vengono servite immediatamente
}

Se invece l’errore 503 è causato da traffico anomalo o da un attacco, il rate limiting sta funzionando correttamente e non va modificato.

Backend non raggiungibile nel blocco upstream

Se nginx è configurato con un blocco upstream che punta a uno o più backend e nessuno di essi è disponibile, restituisce un errore 503

# Messaggio nel log
no live upstreams while connecting to upstream

# Verificare la configurazione upstream
grep -A5 "upstream" /etc/nginx/sites-enabled/il-tuo-sito

# Esempio di configurazione
upstream backend {
    server 127.0.0.1:3000;   # se questa applicazione è ferma → errore 503
    server 127.0.0.1:3001;   # se anche questa è ferma → errore 503
}

La prima verifica da effettuare è se l’applicazione di backend è attiva

ss -tlnp | grep 3000

Se non viene restituito nulla, il processo backend non è in esecuzione

systemctl start nome-servizio
systemctl enable nome-servizio   # per l'avvio automatico

In alternativa, aggiungere un server di backup nella configurazione upstream

upstream backend {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001 backup;   # usato solo se il principale non risponde
}

Disco pieno

Una causa meno ovvia ma frequente, se il disco è pieno nginx e PHP-FPM non riescono a scrivere file temporanei o di log e cominciano a restituire errori, tra cui il 503. Possiamo verificare lo stato del disco

df -h

e se il disco è pieno, le prime operazioni da effettuare possono essere

# pulizia dei log di sistema (journald)
journalctl --vacuum-size=100M

# rotazione forzata dei log Nginx
logrotate -f /etc/logrotate.d/nginx

# ricerca dei file più grandi
du -ah / --max-depth=4 | sort -rh | head -20

Manutenzione programmata

Il 503 è l’unico errore HTTP della famiglia 5xx che può e deve essere usato durante le finestre di manutenzione. In questa situazione, restituire un 503 con un header Retry-After è la pratica corretta in quanto informa i motori di ricerca che il sito è temporaneamente offline e che il contenuto non è stato rimosso, preservando il posizionamento SEO.

Possiamo creare un file /var/www/html/maintenance.html con il messaggio da mostrare agli utenti che va aggiunto nella configurazione di nginx

server {
    listen 80;
    server_name dominio.it www.dominio.it;

    root /var/www/html;

    # File che attiva/disattiva la modalità manutenzione
    if (-f /var/www/html/maintenance.enable) {
        return 503;
    }

    error_page 503 @maintenance;

    location @maintenance {
        rewrite ^(.*)$ /maintenance.html break;
        add_header Retry-After 3600;   # suggerisce di riprovare tra 1 ora
    }
}

Ora, per attivare la manutenzione è sufficiente creare il file “sentinella” definito nella configurazione precedente

# Comandi per attivare la modalità di manutenzione
touch /var/www/html/maintenance.enable
nginx -t && systemctl reload nginx

# Comandi per disattivare la modalità di manutenzione
rm /var/www/html/maintenance.enable
nginx -t && systemctl reload nginx

Questo approccio permette di attivare e disattivare la modalità manutenzione senza modificare ogni volta la configurazione di nginx.