NGINX request-time name resolution
Qualche giorno fa, mi pare fosse il primo di agosto, mi sono accorto che questo sito web era down, insieme a tutte le altre applicazioni web installate sullo stesso server. Ho pensato che fosse andato offline l'intero VPS, invece era UP e perfettamente raggiungibile via ssh.
Una volta collegatomi alla shell, mi sono reso conto che il problema era dovuto al server web, #NGINX, fermo più o meno dalle 6.30 del mattino:
$ systemctl status nginx
× nginx.service - nginx - high performance web server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: failed (Result: exit-code) since Sat 2026-08-01 06:32:54 CEST; 3h 30min ago
Duration: 4d 17min 4.105s
Docs: https://nginx.org/en/docs/
Process: 276634 ExecStart=/usr/sbin/nginx -c ${CONFFILE} (code=exited, status=1/FAILURE)
CPU: 10ms
ago 01 06:32:54 vps-1 nginx[276634]: nginx: [emerg] host not found in upstream "ap.ghost.org" in /etc/nginx/sites-enabled/verolinux.com.conf:22
Il problema
Cos'era successo alle 6? Un attacco hacker aveva compromesso il mio sistema e tutti i miei dati si erano volatilizzati? No!
La causa scatenante è stato l'aggiornamento automatico di alcuni pacchetti di sistema, in particolare libssl, avvenuto pochi secondi prima:
Start-Date: 2026-08-01 06:32:51
Commandline: /usr/bin/unattended-upgrade
Upgrade: libssl3t64:amd64 (3.0.13-0ubuntu3.11, 3.0.13-0ubuntu3.12), openssl:amd64 (3.0.13-0ubuntu3.11, 3.0.13-0ubuntu3.12)
End-Date: 2026-08-01 06:32:53
Infatti, NGINX dipende direttamente da questo pacchetto e, quando viene aggiornato, c'è un sistema che si accorge della presenza della nuova versione e che fa ripartire il servizio:
$ apt-cache show nginx
Package: nginx
Version: 1.31.3-1~noble
Architecture: amd64
Maintainer: NGINX Packaging <nginx-packaging@f5.com>
Installed-Size: 3851
Depends: libc6 (>= 2.34), libcrypt1 (>= 1:4.1.0), libpcre2-8-0 (>= 10.22), libssl3t64 (>= 3.0.0), zlib1g (>= 1:1.1.4), lsb-base (>= 3.0-6)
Il restart di NGINX è una cosa assolutamente normale, ma allora perché è fallito?
All'epoca dei fatti il mio sito girava su #Ghost, che usa delle API per l'implementazione del protocollo ActivityPub. Per farlo, utilizza l'endpoint ap.ghost.org e il mio server, per qualche strano motivo, in quel preciso istante non è riuscito a risolverlo su un indirizzo IP.
nginx, di default, risolve i nomi a dominio configurati in un
upstreamo in unaproxy_passal momento dell'avvio (o del reload). Se la risoluzione fallisce in quel preciso istante, nginx va in erroreemerge non si avvia. Questo comportamento è intenzionale e progettato per garantire che tutte le dipendenze siano raggiungibili, ma può diventare un problema quando la risoluzione DNS di un dominio esterno è temporaneamente instabile.
La soluzione
Per evitare che un blip DNS di un dominio esterno blocchi l'intero server web, si può modificare la configurazione di nginx in modo che risolva il dominio ap.ghost.org al momento della richiesta (request-time) invece che all'avvio.
Per farlo, è sufficiente specificare i resolver all'interno della configurazione del virtual host del sito e sostituire l'hostname con una variabile da utilizzare nelle direttive proxy_pass, come nell'esempio seguente:
resolver 127.0.0.53 1.1.1.1 valid=300s; # <-- definizione del resolver e del TTL
set $ap_upstream ap.ghost.org; # <-- definizione della variabile
location ~ /.ghost/activitypub/* {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
add_header X-Content-Type-Options $header_content_type_options;
proxy_ssl_server_name on;
proxy_pass https://$ap_upstream; # <-- direttiva proxy_pass
}
location ~ /.well-known/(webfinger|nodeinfo) {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
add_header X-Content-Type-Options $header_content_type_options;
proxy_ssl_server_name on;
proxy_pass https://$ap_upstream; # <-- direttiva proxy_pass
}
Con questa configurazione, anche se un hostname non è risolvibile al momento dell'avvio/riavvio di NGINX, il server web parte ugualmente perché la risoluzione vera e propria viene eseguita solo nel momento in cui arriva una richiesta da un client. Come resolver viene indicato quello locale (127.0.0.53) e uno remoto. Il TTL impostato a 300 secondi fa in modo che la risoluzione venga tenuta in cache per 5 minuti dopo una singola richiesta, in modo da non rallentare la risposta in caso di richieste multiple in un dato intervallo di tempo.
Qualora la risoluzione al tempo della richiesta, per qualsiasi motivo, dovesse fallire, NGINX continuerà ad utilizzare il valore della cache.