Configurare Let's Encrypt SSL/TLS per Nginx su Debian 12 e Ubuntu 24.04
Ottieni e rinnova automaticamente certificati TLS gratuiti con Certbot per Nginx su Debian 12 o Ubuntu 24.04. Configurazione DNS, installazione Certbot, redirect HTTP-HTTPS, hardening TLS, HTTP/2, HSTS e la dismissione di OCSP.
Configurare Let's Encrypt SSL/TLS per Nginx su Debian 12 e Ubuntu 24.04
Questo tutorial spiega come ottenere un certificato TLS gratuito da Let's Encrypt tramite Certbot, configurare Nginx per HTTPS e impostare il rinnovo automatico. Ogni passaggio include un comando di verifica per confermare il risultato prima di proseguire.
Se non hai ancora installato Nginx, inizia con Installare Nginx su Debian 12 e Ubuntu 24.04. Per una panoramica sulla gestione di Nginx su un VPS, consulta Amministrazione Nginx su un VPS.
Cosa serve prima di richiedere un certificato?
Prima che Certbot possa emettere un certificato, il tuo dominio deve puntare all'indirizzo IP del server e Nginx deve essere in esecuzione con un server block per quel dominio. Let's Encrypt verifica la proprieta del dominio inviando una richiesta HTTP al tuo server. Se il DNS non risolve verso il tuo VPS o Nginx non e in ascolto, la verifica (challenge) fallisce.
Ti servono:
- Un VPS con Debian 12 o Ubuntu 24.04 e Nginx installato dal repository ufficiale (Installare Nginx su Debian 12 e Ubuntu 24.04)
- Un nome di dominio registrato (useremo
example.comin tutta la guida) - Un record A che punti
example.comall'indirizzo IPv4 del server - Un record AAAA che punti all'indirizzo IPv6 (se il server ne ha uno)
- La porta 80 aperta nel firewall (Certbot usa le HTTP-01 challenge)
- Un server block Nginx funzionante per il tuo dominio (Server Block di Nginx)
Configurare i record DNS
Crea un record A presso il tuo provider DNS:
| Tipo | Nome | Valore | TTL |
|---|---|---|---|
| A | example.com | 203.0.113.10 | 300 |
| AAAA | example.com | 2001:db8::1 | 300 |
Sostituisci gli indirizzi IP con quelli reali del tuo server. Imposta un TTL basso (300 secondi) durante la configurazione per una propagazione rapida. Potrai aumentarlo in seguito.
Verificare la risoluzione DNS
Attendi qualche minuto dopo aver creato i record, poi verifica dalla tua macchina locale (non dal server):
dig +short example.com A
dig +short example.com AAAA
Dovresti vedere gli indirizzi IP del tuo server nell'output. Se non vedi nulla o vedi un IP diverso, il record non si e ancora propagato. Attendi e riprova.
Verifica che Nginx risponda sulla porta 80 dalla tua macchina locale:
curl -I http://example.com
Dovresti ricevere una risposta HTTP/1.1 200 OK con Server: nginx. Se la connessione va in timeout, controlla le regole del firewall.
Come installare Certbot su Debian 12 e Ubuntu 24.04?
Installa Certbot e il suo plugin per Nginx dal repository dei pacchetti della distribuzione usando apt. Il plugin Nginx permette a Certbot di modificare automaticamente i server block per abilitare TLS.
sudo apt update
sudo apt install certbot python3-certbot-nginx -y
Verifica l'installazione:
certbot --version
Su Debian 12 viene installato Certbot 2.1.0. Su Ubuntu 24.04 si ottiene Certbot 2.9.0. Entrambe le versioni funzionano per tutto quello che serve in questo tutorial.
Nota: se hai installato Nginx dal repository ufficiale nginx.org (come raccomandato in Installare Nginx su Debian 12 e Ubuntu 24.04), il plugin Nginx di Certbot funziona senza configurazioni aggiuntive. Rileva i server block in /etc/nginx/conf.d/ e /etc/nginx/sites-enabled/.
Come ottenere un certificato Let's Encrypt per Nginx?
Esegui certbot --nginx con il tuo nome di dominio. Certbot contatta Let's Encrypt, dimostra che controlli il dominio tramite una HTTP-01 challenge, ottiene il certificato e modifica il server block di Nginx per utilizzarlo. L'intero processo richiede circa 30 secondi.
sudo certbot --nginx -d example.com -d www.example.com
Certbot chiedera il tuo indirizzo email (per i promemoria di rinnovo) e l'accettazione dei Termini di Servizio. Dopo:
- Posiziona un file di HTTP-01 challenge nella web root
- Chiede a Let's Encrypt di verificarlo
- Scarica il certificato firmato
- Modifica il server block di Nginx aggiungendo le direttive TLS
- Ricarica Nginx
Verifica che il certificato sia stato emesso:
sudo ls -la /etc/letsencrypt/live/example.com/
Dovresti vedere:
lrwxrwxrwx 1 root root ... cert.pem -> ../../archive/example.com/cert1.pem
lrwxrwxrwx 1 root root ... chain.pem -> ../../archive/example.com/chain1.pem
lrwxrwxrwx 1 root root ... fullchain.pem -> ../../archive/example.com/fullchain1.pem
lrwxrwxrwx 1 root root ... privkey.pem -> ../../archive/example.com/privkey1.pem
Questi sono link simbolici. fullchain.pem contiene il certificato e la catena CA intermedia. privkey.pem e la chiave privata.
Controlla che Nginx sia in esecuzione con la nuova configurazione:
sudo nginx -t && sudo systemctl status nginx
nginx -t testa la sintassi della configurazione. Se stampa test is successful, la configurazione e valida.
Cosa modifica Certbot nella configurazione di Nginx?
Certbot aggiunge diverse righe al server block. Ecco cosa inserisce (le righe contrassegnate da # managed by Certbot):
server {
server_name example.com www.example.com;
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
# ... i tuoi location block esistenti ...
}
Il file options-ssl-nginx.conf contiene le impostazioni TLS predefinite di Certbot. Le sostituiremo con impostazioni piu sicure nella sezione di hardening qui sotto.
Certbot crea anche un secondo server block per il redirect da HTTP a HTTPS. Miglioreremo quel redirect nella prossima sezione.
Puoi vedere esattamente cosa e cambiato confrontando la configurazione:
sudo diff /etc/nginx/conf.d/example.com.conf /etc/nginx/conf.d/example.com.conf.bak 2>/dev/null || echo "No backup found. Certbot modifies in place."
Come fare il redirect da HTTP a HTTPS in Nginx?
Tutto il traffico HTTP dovrebbe essere rediretto verso HTTPS con un redirect 301 (permanente). Certbot potrebbe aggiungerlo automaticamente, ma il suo comportamento predefinito usa un'istruzione if dentro il server block esistente. Questo e un anti-pattern in Nginx. Un server block dedicato e piu pulito e affidabile.
Sostituisci il redirect di Certbot con un server block separato. Modifica il file di configurazione (il percorso dipende dalla tua configurazione; tipicamente /etc/nginx/conf.d/example.com.conf):
# HTTP -> HTTPS redirect (server block separato, non un if-statement)
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
Questo va nello stesso file del server block HTTPS, o in un file separato. Assicurati di rimuovere qualsiasi blocco di redirect generato da Certbot per evitare duplicati.
Testa e ricarica:
sudo nginx -t
sudo systemctl reload nginx
Verifica il redirect dalla tua macchina locale:
curl -I http://example.com
Output atteso:
HTTP/1.1 301 Moved Permanently
Location: https://example.com/
Come rafforzare le impostazioni TLS per un server di produzione?
La configurazione TLS predefinita di Certbot (options-ssl-nginx.conf) e volutamente conservativa. Per un server di produzione servono impostazioni piu restrittive. Seguiremo il profilo Intermediate di Mozilla dal SSL Configuration Generator, che bilancia sicurezza e compatibilita con i client fino a Firefox 27, Chrome 31 e Android 4.4.2.
Crea un file snippet che puoi includere con include in ogni server block:
sudo nano /etc/nginx/snippets/tls-params.conf
Aggiungi il contenuto seguente:
# TLS protocol versions — TLS 1.2 and 1.3 only
# TLS 1.0 and 1.1 are deprecated (RFC 8996)
ssl_protocols TLSv1.2 TLSv1.3;
# Ciphers — Mozilla Intermediate profile (January 2026)
# Source: https://ssl-config.mozilla.org/
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
# DH parameters — 2048-bit, RFC 7919 ffdhe2048
ssl_dhparam /etc/nginx/dhparam.pem;
# Session settings
ssl_session_timeout 1d;
ssl_session_cache shared:TLS:10m;
ssl_session_tickets off;
# HSTS — tell browsers to always use HTTPS (2 years)
# Only add includeSubDomains if ALL subdomains use HTTPS
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
# Hide Nginx version in error pages and headers
server_tokens off;
Genera il file dei parametri DH (richiede qualche secondo):
sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048
Verifica che il file sia stato creato:
sudo ls -la /etc/nginx/dhparam.pem
Ora aggiorna il server block HTTPS per usare queste impostazioni al posto di quelle predefinite di Certbot. Rimuovi la riga include /etc/letsencrypt/options-ssl-nginx.conf; e la riga ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;. Sostituiscile con:
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# TLS hardening (replaces Certbot defaults)
include snippets/tls-params.conf;
# ... your location blocks ...
}
Testa e ricarica:
sudo nginx -t
sudo systemctl reload nginx
Quali versioni TLS e cipher usare?
Mozilla pubblica tre profili TLS. Ecco il confronto:
| Profilo | Protocolli | Client compatibile piu vecchio | Caso d'uso |
|---|---|---|---|
| Modern | Solo TLS 1.3 | Firefox 63, Chrome 70, Android 10 | Servizi dove tutti i client sono recenti |
| Intermediate | TLS 1.2 + 1.3 | Firefox 27, Chrome 31, Android 4.4 | Web server generici |
| Old | TLS 1.0 + 1.1 + 1.2 + 1.3 | Firefox 1, Chrome 1, IE 8 | Solo sistemi legacy |
Usa Intermediate a meno che tu non abbia una ragione specifica per non farlo. Copre il 99,9%+ dei browser attuali escludendo i protocolli deboli. TLS 1.0 e 1.1 sono stati formalmente deprecati dalla RFC 8996 a marzo 2021.
La lista cipher nel nostro snippet usa solo cipher AEAD (GCM e ChaCha20-Poly1305). ssl_prefer_server_ciphers off lascia al client la scelta del cipher preferito. Questa e la raccomandazione di Mozilla perche i client moderni fanno scelte migliori rispetto a una preferenza statica del server.
Let's Encrypt supporta ancora l'OCSP stapling?
No. Let's Encrypt ha dismesso il proprio servizio OCSP il 6 agosto 2025. I certificati emessi dopo maggio 2025 non contengono URL del responder OCSP. Lo stato di revoca viene ora pubblicato esclusivamente tramite Certificate Revocation Lists (CRL). Se usi solo certificati Let's Encrypt, rimuovi tutte le direttive ssl_stapling dalla configurazione di Nginx.
Ecco la cronologia:
- Dicembre 2024. Let's Encrypt ha annunciato il piano per terminare OCSP.
- 30 gennaio 2025. I certificati che richiedevano l'estensione OCSP Must-Staple hanno iniziato a fallire.
- 7 maggio 2025. I nuovi certificati hanno smesso di includere URL del responder OCSP. Al loro posto sono stati aggiunti URL CRL.
- 6 agosto 2025. Il servizio OCSP e stato completamente dismesso. Tutti i certificati emessi in precedenza con URL OCSP sono ormai scaduti.
Se vedi ssl_stapling on; o ssl_stapling_verify on; in qualche guida o snippet di configurazione, si tratta di consigli obsoleti per gli utenti Let's Encrypt. Queste direttive sono innocue (Nginx le ignora silenziosamente quando non c'e un URL del responder OCSP), ma aggiungono confusione inutile. Rimuovile.
Se usi certificati da una CA diversa che fornisce ancora OCSP, quelle direttive restano valide per quei certificati.
Perche Let's Encrypt ha abbandonato OCSP? Due motivi. OCSP e un rischio per la privacy: ogni volta che un browser verificava la revoca del certificato tramite OCSP, la CA veniva a sapere quale sito veniva visitato e da quale IP. Al picco, il servizio OCSP di Let's Encrypt gestiva 340 miliardi di richieste al mese. Il passaggio alle CRL elimina questa fuga di dati. Le CRL vengono scaricate in blocco dai browser, quindi nessuna richiesta per singola visita viene inviata alla CA.
Come abilitare HTTP/2 con Nginx?
HTTP/2 multiplexa le richieste su una singola connessione, riducendo la latenza. Se hai installato Nginx dal repository ufficiale nginx.org (versione 1.25.1 o successiva), abilita HTTP/2 con la direttiva http2 dentro il server block.
Aggiungi http2 on; nel server block HTTPS:
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include snippets/tls-params.conf;
# ... your location blocks ...
}
La direttiva http2 on; ha sostituito la sintassi deprecata listen 443 ssl http2; a partire da Nginx 1.25.1. La vecchia sintassi funziona ancora ma produce avvisi di deprecazione nel log degli errori. Se usi la versione di Nginx dei repository della distribuzione (1.22 su Debian 12, 1.24 su Ubuntu 24.04), usa la vecchia sintassi listen 443 ssl http2;.
Testa e ricarica:
sudo nginx -t
sudo systemctl reload nginx
Verifica che HTTP/2 sia attivo:
curl -I --http2 -s https://example.com | head -1
Output atteso:
HTTP/2 200
Se vedi HTTP/1.1, controlla che http2 on; sia nel server block corretto e che la tua versione di Nginx lo supporti.
Come funziona il rinnovo automatico dei certificati?
I certificati Let's Encrypt scadono dopo 90 giorni. Certbot installa un timer systemd (certbot.timer) che controlla due volte al giorno se un certificato e a meno di 30 giorni dalla scadenza. In caso positivo, lo rinnova automaticamente. Non serve impostare un cron job.
Verifica che il timer sia attivo:
systemctl status certbot.timer
Dovresti vedere Active: active (waiting) e una riga che mostra il prossimo orario di attivazione.
Controlla quando avverra il prossimo rinnovo:
systemctl list-timers certbot.timer
Mostra gli orari NEXT e LAST di esecuzione.
Testare il rinnovo senza rinnovare davvero
Esegui un dry-run per verificare che il processo di rinnovo funzioni dall'inizio alla fine:
sudo certbot renew --dry-run
Questo contatta il server staging di Let's Encrypt e simula un rinnovo completo. Se stampa Congratulations, all simulated renewals succeeded, la configurazione e corretta.
Configurare un deploy hook per ricaricare Nginx
Quando Certbot rinnova un certificato, Nginx deve essere ricaricato per utilizzare i nuovi file. Configura un deploy hook che viene eseguito solo dopo un rinnovo riuscito:
sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy
sudo nano /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
Aggiungi:
#!/bin/bash
/usr/bin/systemctl reload nginx
Rendilo eseguibile:
sudo chmod 700 /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
Verifica i permessi:
ls -la /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
L'output atteso mostra -rwx------ (solo root puo leggere ed eseguire).
La directory deploy esegue gli script solo dopo un rinnovo riuscito, non a ogni attivazione del timer. Questo evita ricaricamenti inutili.
Come verificare che la configurazione TLS sia corretta?
Dopo aver completato tutti i passaggi precedenti, esegui questi comandi di verifica. Ognuno controlla un aspetto diverso della configurazione.
Controllare la catena dei certificati con OpenSSL
Dalla tua macchina locale:
openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | openssl x509 -noout -dates -subject -issuer
Output atteso:
notBefore=Mar 19 00:00:00 2026 GMT
notAfter=Jun 17 00:00:00 2026 GMT
subject=CN = example.com
issuer=C = US, O = Let's Encrypt, CN = R12
Nota: la data notAfter dovrebbe essere circa 90 giorni dopo l'emissione. L'emittente (issuer) dovrebbe essere Let's Encrypt. Gli intermediari RSA attuali sono R12 e R13. Per i certificati ECDSA, cerca E7 o E8.
Controllare la versione TLS e il cipher in uso
openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | grep -E "Protocol|Cipher"
Risultato atteso:
Protocol : TLSv1.3
Cipher : TLS_AES_256_GCM_SHA384
Se vedi TLSv1.2, va bene lo stesso. Dipende da quale versione preferisce il tuo OpenSSL locale.
Controllare gli header HTTPS
curl -I https://example.com
Cerca:
HTTP/2 200
strict-transport-security: max-age=63072000; includeSubDomains
Se strict-transport-security manca, verifica la riga add_header nel tuo snippet TLS. Il parametro always garantisce che l'header venga inviato anche nelle risposte di errore.
Riepilogo dei comandi di verifica
| Comando | Cosa controlla | Cosa cercare |
|---|---|---|
dig +short example.com |
Risoluzione DNS | L'IP del tuo server |
curl -I http://example.com |
Redirect HTTP | 301 con Location: https:// |
curl -I https://example.com |
HTTPS + header | HTTP/2 200, header HSTS |
openssl s_client -connect ... |
Catena certificati, versione TLS | Emittente Let's Encrypt, TLSv1.2 o 1.3 |
certbot renew --dry-run |
Processo di rinnovo | all simulated renewals succeeded |
systemctl status certbot.timer |
Timer rinnovo automatico | active (waiting) |
Test con SSL Labs
Per un audit esterno approfondito, invia il tuo dominio a SSL Labs Server Test. Con la configurazione di questa guida, dovresti ottenere un punteggio A o A+. Il punteggio A+ richiede HSTS, che abbiamo abilitato nello snippet TLS.
Configurazione Nginx di riferimento completa
Ecco il server block completo con tutte le impostazioni di questo tutorial combinate:
# HTTP -> HTTPS redirect
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
# HTTPS server block
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name example.com www.example.com;
# Let's Encrypt certificate
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# TLS hardening
include snippets/tls-params.conf;
root /var/www/example.com/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
# Deny access to hidden files
location ~ /\. {
deny all;
}
}
E lo snippet TLS in /etc/nginx/snippets/tls-params.conf:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
ssl_dhparam /etc/nginx/dhparam.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:TLS:10m;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
server_tokens off;
Per header di sicurezza aggiuntivi come Content-Security-Policy, X-Frame-Options e Permissions-Policy, consulta Hardening della Sicurezza Nginx.
Qualcosa non funziona?
Certbot dice "Could not automatically find a matching server block"
Certbot cerca una direttiva server_name che corrisponda al dominio specificato con -d. Assicurati che il file del server block sia in /etc/nginx/conf.d/ o /etc/nginx/sites-enabled/ e contenga server_name example.com;.
Certbot dice "Connection refused" o "Challenge failed" La porta 80 deve essere aperta. Controlla il firewall:
sudo ufw status # if using UFW
sudo nft list ruleset # if using nftables
Verifica anche che Nginx sia in ascolto sulla porta 80:
sudo ss -tlnp | grep ':80'
"SSL: error" nel log errori di Nginx dopo aver modificato le impostazioni TLS Probabilmente c'e un errore di sintassi nella stringa dei cipher o un percorso file mancante. Controlla:
sudo nginx -t
sudo journalctl -u nginx -n 20 --no-pager
Il browser mostra "NET::ERR_CERT_DATE_INVALID" Il certificato potrebbe essere scaduto. Controlla la scadenza:
sudo certbot certificates
Se e scaduto, forza il rinnovo:
sudo certbot renew --force-renewal
certbot renew --dry-run fallisce
Causa frequente: il DNS del dominio non punta piu a questo server, oppure la porta 80 e bloccata. Certbot necessita dell'accesso HTTP-01 per il rinnovo.
HTTP/2 non funziona
Controlla la versione di Nginx: nginx -v. Se e inferiore alla 1.25.1, usa listen 443 ssl http2; al posto della direttiva separata http2 on;. Se e 1.25.1 o successiva, assicurati che http2 on; sia dentro il blocco server corretto (non in http o location).
Copyright 2026 Virtua.Cloud. Tutti i diritti riservati. Questo contenuto e un'opera originale del team Virtua.Cloud. La riproduzione, ripubblicazione o redistribuzione senza autorizzazione scritta e vietata.
Pronto a provare?
Distribuisci il tuo server in pochi secondi. Linux, Windows o FreeBSD.
Vedi piani VPS