Come Configurare un Certificato SSL Gratis su Nginx con Let’s Encrypt: Guida Passo-Passo

Attivare un certificato SSL gratis su Nginx con Let’s Encrypt richiede meno di dieci minuti, ma la differenza tra una configurazione “che funziona” e una configurazione solida sta nei dettagli: il redirect corretto da HTTP a HTTPS, il rinnovo automatico realmente testato e gli header di sicurezza giusti.

In questa guida operativa trovi l’intero percorso, dall’installazione di Certbot fino al voto A+ su SSL Labs, con i comandi pronti da copiare e una sezione dedicata alla risoluzione degli errori più comuni (in primis la challenge fallita sulla porta 80). Come lettura successiva va bene Certificato SSL Let’s Encrypt: cos’è e come funziona.

Cosa ti serve prima di iniziare

  • Un server Linux (Ubuntu 24.04 LTS, Debian 12/13, Rocky Linux 9 o derivate) con accesso root o sudo.
  • Nginx già installato e funzionante (nginx -v per verificare).
  • Un dominio valido con record DNS A (e AAAA se usi IPv6) che punta all’IP pubblico del server.
  • Le porte 80 e 443 aperte su firewall e security group del provider.
  • Un indirizzo email valido per gli avvisi tecnici dell’account ACME.

Verifica rapida del DNS prima di lanciare qualsiasi comando:

dig +short esempio.it
dig +short www.esempio.it
curl -I http://esempio.it

Se l’IP restituito non è quello del tuo server, fermati qui: la validazione HTTP-01 fallirà in ogni caso.

ssl padlock security

Let’s Encrypt e Certbot: come funzionano davvero

Let’s Encrypt è una Certificate Authority gratuita e automatizzata gestita dalla non-profit ISRG. Rilascia certificati DV (Domain Validation) attraverso il protocollo ACME: il client dimostra il controllo del dominio e la CA firma il certificato, senza intervento umano e senza costi.

Certbot è il client ACME ufficiale, mantenuto dalla EFF. Esistono alternative valide (acme.sh, lego, Caddy integrato), ma Certbot resta la scelta più documentata e con il plugin Nginx più maturo.

I tre metodi di validazione

Challenge Come funziona Quando usarla
HTTP-01 File temporaneo servito su /.well-known/acme-challenge/ sulla porta 80 Caso standard, sito pubblico raggiungibile
DNS-01 Record TXT _acme-challenge nella zona DNS Certificati wildcard, server non esposti, LAN interne
TLS-ALPN-01 Handshake TLS dedicato sulla porta 443 Porta 80 bloccata dal provider

Passo 1: installare Certbot su Nginx

Metodo consigliato: snap (sempre aggiornato)

sudo apt update
sudo apt install -y snapd
sudo snap install core && sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
certbot --version

Metodo alternativo: pacchetti di sistema

Su Debian/Ubuntu:

sudo apt install -y certbot python3-certbot-nginx

Su Rocky Linux / AlmaLinux:

sudo dnf install -y epel-release
sudo dnf install -y certbot python3-certbot-nginx

Nota: i pacchetti distro sono spesso indietro di qualche versione. Se devi usare funzionalità recenti (profili di certificato, nuovi plugin DNS), lo snap è la strada più sicura.

ssl padlock security

Passo 2: preparare il server block Nginx

Certbot deve trovare un server_name corrispondente al dominio. Crea il file /etc/nginx/sites-available/esempio.it:

server {
    listen 80;
    listen [::]:80;
    server_name esempio.it www.esempio.it;

    root /var/www/esempio.it/public;
    index index.html index.php;

    location /.well-known/acme-challenge/ {
        root /var/www/html;
        allow all;
    }

    location / {
        try_files $uri $uri/ =404;
    }
}

Attiva e ricarica:

sudo ln -s /etc/nginx/sites-available/esempio.it /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Apri le porte sul firewall:

# UFW
sudo ufw allow 'Nginx Full'

# firewalld
sudo firewall-cmd --permanent --add-service={http,https}
sudo firewall-cmd --reload

Passo 3: emettere il certificato SSL gratuito

Modalità automatica (plugin Nginx)

Certbot modifica da solo la configurazione e inserisce il redirect:

sudo certbot --nginx -d esempio.it -d www.esempio.it \
  --email [email protected] --agree-tos --no-eff-email --redirect

Più domini nello stesso certificato (SAN)

Un singolo certificato può coprire fino a 100 nomi host. Basta ripetere il flag -d:

sudo certbot --nginx \
  -d esempio.it -d www.esempio.it \
  -d shop.esempio.it -d api.esempio.it \
  --agree-tos --no-eff-email --email [email protected]

Tutti i domini devono puntare allo stesso server e risultare validabili, altrimenti l’intera emissione fallisce.

Modalità webroot (zero modifiche automatiche)

Se preferisci mantenere il pieno controllo del file di configurazione, usa il plugin webroot: Certbot scrive solo il file di challenge e non tocca Nginx. Questo tipo di lavoro non crea problemi a uno studio di web design.

sudo mkdir -p /var/www/html/.well-known/acme-challenge
sudo certbot certonly --webroot -w /var/www/html \
  -d esempio.it -d www.esempio.it

Certificato wildcard con DNS-01

sudo certbot certonly --manual --preferred-challenges dns \
  -d esempio.it -d "*.esempio.it"

Per l’automazione completa esistono plugin dedicati ai principali provider DNS (Cloudflare, Route53, OVH, DigitalOcean). Esempio con Cloudflare:

sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-cloudflare
sudo certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
  -d esempio.it -d "*.esempio.it"

I file generati si trovano in /etc/letsencrypt/live/esempio.it/:

  • fullchain.pem: certificato + catena intermedia (è quello che serve a Nginx)
  • privkey.pem: chiave privata
  • cert.pem e chain.pem: componenti separate, raramente necessarie

Passo 4: configurazione Nginx completa con redirect HTTP → HTTPS

Questa è la configurazione che usiamo in produzione, pensata per ottenere un punteggio A+ su SSL Labs:

# --- Redirect HTTP verso HTTPS ---
server {
    listen 80;
    listen [::]:80;
    server_name esempio.it www.esempio.it;

    location /.well-known/acme-challenge/ {
        root /var/www/html;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

# --- Server HTTPS ---
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name esempio.it www.esempio.it;

    root /var/www/esempio.it/public;
    index index.php index.html;

    ssl_certificate     /etc/letsencrypt/live/esempio.it/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/esempio.it/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
    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;

    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;
    ssl_session_tickets off;

    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
}

Applica e verifica:

sudo nginx -t && sudo systemctl reload nginx

Il dettaglio che quasi nessuna guida aggiorna: OCSP stapling

Molti tutorial ancora in circolazione consigliano di attivare ssl_stapling on;. Let’s Encrypt ha dismesso il servizio OCSP, quindi quelle direttive oggi generano solo warning nei log di Nginx senza alcun beneficio. Se le trovi nelle tue configurazioni, rimuovile:

# DA RIMUOVERE sui certificati Let's Encrypt
# ssl_stapling on;
# ssl_stapling_verify on;
# resolver 1.1.1.1 8.8.8.8 valid=300s;

La revoca viene oggi gestita tramite CRL lato browser (meccanismi come CRLite), non più tramite risposta OCSP allegata all’handshake.

HSTS preload: attenzione

Aggiungi il flag preload all’header HSTS solo quando sei certo che tutti i sottodomini funzioneranno in HTTPS per anni. L’inserimento nella preload list dei browser è difficile da annullare.

ssl padlock security

Passo 5: rinnovo automatico (cron o systemd timer)

I certificati Let’s Encrypt durano 90 giorni e il rinnovo parte automaticamente quando mancano meno di 30 giorni alla scadenza. Dal 2025 Let’s Encrypt ha inoltre smesso di inviare le email di avviso scadenza: l’automazione non è opzionale.

Verifica prima se il timer esiste già

Le installazioni via snap e via apt creano in autonomia un systemd timer:

systemctl list-timers | grep certbot
sudo systemctl status snap.certbot.renew.timer

Se il timer è attivo, non aggiungere un cron job: duplicheresti i tentativi inutilmente.

Test a secco (obbligatorio)

sudo certbot renew --dry-run

Un output con Congratulations, all simulated renewals succeeded conferma che il rinnovo funzionerà davvero.

Cron job manuale

Se il tuo sistema non usa systemd o preferisci cron:

sudo crontab -e

Aggiungi:

17 3,15 * * * /usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"

Perché due esecuzioni al giorno a un minuto “strano”: Let’s Encrypt chiede esplicitamente di randomizzare l’orario per non sovraccaricare i server ACME alle ore tonde.

Deploy hook vs post hook

Hook Quando viene eseguito Uso tipico
--pre-hook Prima di ogni tentativo Fermare un servizio in modalità standalone
--deploy-hook Solo se il certificato è stato rinnovato Reload di Nginx, copia su altri nodi
--post-hook Dopo ogni tentativo, anche senza rinnovo Riavviare servizi fermati nel pre-hook

Per rendere permanente l’hook senza dipendere dal cron, crea /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh:

#!/bin/bash
/usr/bin/systemctl reload nginx
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

Monitoraggio della scadenza

sudo certbot certificates

echo | openssl s_client -connect esempio.it:443 -servername esempio.it 2>/dev/null \
  | openssl x509 -noout -dates

Passo 6: verificare la configurazione con SSL Labs

Apri https://www.ssllabs.com/ssltest/ e inserisci il dominio. Spunta “Do not show the results on the boards” se non vuoi che il test resti pubblico. Cosa controllare:

  1. Certificate: catena completa, nessun “Chain issues: incomplete”. Se compare, hai puntato cert.pem invece di fullchain.pem.
  2. Protocol Support: solo TLS 1.2 e TLS 1.3 attivi.
  3. Key Exchange e Cipher Strength: entrambi al 90 o 100%.
  4. HSTS: deve risultare Yes con max-age di almeno 6 mesi.

Controlli complementari da riga di comando:

# Catena e protocolli
nmap --script ssl-enum-ciphers -p 443 esempio.it

# Redirect 301 corretto
curl -sI http://esempio.it | head -n 5

# Scansione completa
docker run --rm -ti drwetter/testssl.sh https://esempio.it

Non dimenticare la parte applicativa: mixed content. Cerca riferimenti http:// hardcoded in temi, CSS e database, altrimenti il lucchetto resterà incompleto anche con un certificato perfetto.

Risoluzione degli errori più comuni

Errore Causa Soluzione
Timeout during connect (likely firewall problem) Porta 80 chiusa o filtrata Apri la 80 su firewall locale e sul pannello cloud; verifica con nc -zv IP 80 dall’esterno
404 Not Found sulla challenge Webroot errato o location che blocca i file nascosti Allinea -w alla root reale ed escludi /.well-known/ dalle regole location ~ /\.
DNS problem: NXDOMAIN Record A mancante o non propagato Controlla con dig e attendi la propagazione prima di riprovare
Invalid response ... "<!DOCTYPE html>" Un redirect o una SPA intercetta tutte le richieste Inserisci il blocco /.well-known/acme-challenge/ prima del redirect verso HTTPS
Could not bind to IPv4 or IPv6 (standalone) Nginx occupa già la porta 80 Usa --nginx o --webroot, oppure ferma Nginx con i pre/post hook
too many certificates already issued Rate limit settimanale superato Attendi la finestra oraria e usa --dry-run o l’ambiente di staging per i test
Browser: NET::ERR_CERT_AUTHORITY_INVALID Catena intermedia assente Punta a fullchain.pem, mai a cert.pem
Redirect loop infinito CDN o proxy in modalità Flexible SSL Imposta la modalità Full (strict) e gestisci X-Forwarded-Proto

Il caso più frequente: challenge fallita sulla porta 80

Nove volte su dieci la causa è una di queste. Procedi in ordine:

  1. Testa la raggiungibilità dall’esterno (non dal server stesso): curl -I http://esempio.it/.well-known/acme-challenge/test
  2. Crea un file di prova e verifica che sia servito: echo ok | sudo tee /var/www/html/.well-known/acme-challenge/test
  3. Controlla i log in tempo reale: sudo tail -f /var/log/nginx/access.log durante l’esecuzione di Certbot. Se non vedi la richiesta dei server ACME, il traffico non arriva al server e il problema è di rete, non di Nginx.
  4. Verifica i permessi: sudo chown -R www-data:www-data /var/www/html/.well-known
  5. Se il provider blocca definitivamente la porta 80, passa a DNS-01 o a TLS-ALPN-01.

Per i test ripetuti usa sempre l’ambiente di staging, che non consuma i rate limit:

sudo certbot --nginx -d esempio.it --dry-run
# oppure
sudo certbot certonly --staging --webroot -w /var/www/html -d esempio.it
ssl padlock security

Casi particolari

Nginx come reverse proxy

Quando Nginx fa da frontend verso un’applicazione su altra porta, ricorda di passare lo schema corretto al backend, altrimenti otterrai loop di redirect:

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Server in LAN o non esposti su internet

Per servizi interni (homelab, ambienti di staging, Nginx Proxy Manager) l’unica strada è la challenge DNS-01 con un dominio pubblico delegato a un provider DNS supportato: il certificato viene emesso senza che il server sia raggiungibile dall’esterno. Su Let’s Encrypt completa bene quanto sopra.

Revocare o eliminare un certificato

sudo certbot revoke --cert-path /etc/letsencrypt/live/esempio.it/cert.pem
sudo certbot delete --cert-name esempio.it

Checklist finale

  • ✅ https://esempio.it risponde con lucchetto valido
  • ✅ http://esempio.it restituisce un 301 verso HTTPS
  • ✅ Voto A o A+ su SSL Labs, senza chain issues
  • ✅ certbot renew --dry-run completato con successo
  • ✅ Timer systemd attivo oppure cron job configurato con deploy hook
  • ✅ Nessun mixed content nella console del browser
  • ✅ Sitemap, canonical e Search Console aggiornati alla versione HTTPS

FAQ

Come posso ottenere un certificato SSL gratuito?

Il modo più diffuso è Let’s Encrypt tramite Certbot: installi il client sul server, lanci certbot --nginx -d tuodominio.it e il certificato viene emesso e installato in pochi secondi. Altre CA gratuite sono ZeroSSL e Buypass Go SSL, utilizzabili con gli stessi client ACME.

Quanto dura un certificato Let’s Encrypt?

La durata standard è di 90 giorni. Il rinnovo automatico scatta quando mancano 30 giorni alla scadenza. Let’s Encrypt ha introdotto anche profili con certificati a vita molto breve (circa 6 giorni), pensati per infrastrutture completamente automatizzate.

Quanto costa un certificato SSL?

Con Let’s Encrypt il costo è zero, anche per i wildcard. I certificati commerciali DV partono da poche decine di euro l’anno, mentre OV ed EV possono superare i 200 euro: hanno senso solo quando serve validazione dell’organizzazione o garanzie contrattuali, non per un miglior livello di cifratura, che è identico.

Let’s Encrypt è adatto a un sito e-commerce?

Sì. La cifratura è la stessa dei certificati a pagamento e i browser non fanno alcuna distinzione visiva. Valuta un certificato OV solo se il tuo settore o i tuoi clienti richiedono esplicitamente la verifica dell’identità aziendale.

Devo rigenerare il certificato se aggiungo un sottodominio?

Sì, va riemesso includendo tutti i nomi: sudo certbot --nginx --cert-name esempio.it -d esempio.it -d www.esempio.it -d nuovo.esempio.it. In alternativa, un certificato wildcard copre tutti i sottodomini di primo livello. virtua.cloud ha trattato il tema a lungo.

Il passaggio a HTTPS influisce sulla SEO?

HTTPS è un fattore di ranking confermato da Google. L’importante è gestire la migrazione correttamente: redirect 301 permanenti, aggiornamento dei link interni e dei canonical, nuova property HTTPS in Search Console e sitemap aggiornata.

Cosa succede se dimentico di rinnovare?

Il certificato scade e i visitatori vedranno un avviso di sicurezza a tutto schermo, con conseguente crollo del traffico. Poiché gli avvisi email di scadenza non vengono più inviati, è indispensabile verificare periodicamente lo stato del timer di rinnovo o impostare un monitoraggio esterno sulla data di scadenza.


Hai bisogno di supporto nella configurazione del tuo server, nella migrazione a HTTPS o nell’hardening della tua infrastruttura web? Il team di Alede Design è a disposizione per analizzare il tuo caso specifico.

Leave a Comment