È uscito il Corso Java Completo
Torna al blog

Let's Encrypt: certificati HTTPS gratuiti

Come funziona Let's Encrypt: validazione ACME, certbot, rinnovo automatico, certificati wildcard con DNS-01 e gli errori che fanno scadere il certificato.

Edoardo Midali

Edoardo Midali

Developer · Content Creator

9 min di lettura

Il sito è online, funziona, e il browser mostra "Non sicuro" accanto all'indirizzo. Fino a qualche anno fa la soluzione costava qualche decina di euro l'anno e mezza giornata di trafila burocratica; oggi sono due comandi e zero euro. In questo articolo trovi come Let's Encrypt rilascia i certificati, come si usa certbot, come funziona il rinnovo automatico, come ottenere un wildcard — e i quattro errori che fanno scadere il certificato nel momento peggiore.

Cos'è

Let's Encrypt è un'autorità di certificazione gratuita che rilascia certificati HTTPS in modo completamente automatico, verificando via software che tu controlli davvero il dominio.

I browser la riconoscono esattamente come una qualunque autorità a pagamento: il lucchetto è lo stesso, la crittografia è la stessa. Le differenze pratiche sono due: i certificati durano 90 giorni invece di un anno, e non esistono certificati con validazione dell'organizzazione — solo la conferma che quel dominio è tuo.

I 90 giorni non sono un limite, sono il punto. Una durata breve obbliga ad automatizzare il rinnovo, e un rinnovo automatico è più affidabile di un promemoria annuale che nessuno guarda.

Come funziona la validazione (ACME)

ACME è il protocollo con cui il tuo server e Let's Encrypt si parlano. La logica è semplice: Let's Encrypt ti chiede di dimostrare che controlli il dominio, mettendo qualcosa in un posto che solo il proprietario può raggiungere.

Il flusso è sempre lo stesso:

  1. Il tuo software (certbot o simile) chiede un certificato per miosito.it.
  2. Let's Encrypt risponde con una sfida: un valore casuale da pubblicare.
  3. Il software pubblica quel valore.
  4. Let's Encrypt lo va a leggere dall'esterno.
  5. Se lo trova, emette il certificato.

Ci sono due modi per pubblicare il valore, ed è la scelta che determina tutto il resto.

HTTP-01

Il valore viene messo in un file sotto http://miosito.it/.well-known/acme-challenge/. Let's Encrypt lo scarica in HTTP puro, sulla porta 80.

È il metodo predefinito e il più semplice. Ha tre requisiti non negoziabili:

  • Il dominio deve già puntare all'IP del server (collegare il dominio viene prima di questo).
  • La porta 80 deve essere raggiungibile da internet.
  • Non funziona per i certificati wildcard.

DNS-01

Il valore viene pubblicato come record TXT nel DNS del dominio, sotto _acme-challenge.miosito.it. Let's Encrypt interroga il DNS invece del server web.

È più laborioso da configurare — serve accesso via API al provider DNS — ma è l'unico metodo che permette i wildcard, e funziona anche se il server non è raggiungibile dall'esterno o se la porta 80 è chiusa.

HTTP-01DNS-01
Cosa servePorta 80 apertaAccesso API al DNS
WildcardNo
Server non pubblicoNo
ConfigurazioneImmediataUn plugin per provider
QuandoCaso normaleWildcard, reti interne

Certbot in pratica

Certbot è il client ACME ufficiale. Sulla maggior parte dei server è il percorso più corto.

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d miosito.it -d www.miosito.it

Con --nginx, certbot non si limita a ottenere il certificato: modifica da solo la configurazione di Nginx, aggiunge il blocco per la porta 443, imposta il reindirizzamento da HTTP a HTTPS e ricarica il servizio. Per Apache esiste python3-certbot-apache e la stessa cosa con --apache.

Se preferisci gestire la configurazione a mano, chiedi solo il certificato:

sudo certbot certonly --webroot -w /var/www/miosito -d miosito.it

I file finiscono in /etc/letsencrypt/live/miosito.it/:

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

Usa sempre fullchain.pem, non cert.pem. È l'errore più subdolo di tutti: con cert.pem manca la catena intermedia, il sito funziona perfettamente su Chrome desktop e fallisce su Android vecchi, su curl e su parecchi client API. Nessun avviso ti dice che hai scelto il file sbagliato.

Verifica il risultato dall'esterno, non dal browser che hai già usato:

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

Il rinnovo automatico

Certbot installa da solo un timer che tenta il rinnovo due volte al giorno. Il rinnovo avviene davvero solo quando mancano meno di 30 giorni alla scadenza, quindi ci sono decine di tentativi prima che il certificato diventi un problema.

systemctl list-timers | grep certbot     # il timer c'è?
sudo certbot renew --dry-run             # prova senza emettere nulla

--dry-run è il comando che devi eseguire dopo ogni cambio di configurazione del server. Simula l'intero rinnovo contro l'ambiente di test di Let's Encrypt, senza consumare quota. Se passa, il rinnovo vero funzionerà.

Se gestisci la configurazione a mano, ricorda che rinnovare il certificato non ricarica il servizio: fino al riavvio, il server continua a servire il vecchio file in memoria.

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

Gli script in deploy/ girano solo quando un certificato è stato effettivamente rinnovato. Il funzionamento dei timer è spiegato in cos'è systemd.

I certificati wildcard

Un wildcard (*.miosito.it) copre tutti i sottodomini di primo livello con un certificato solo. Richiede DNS-01, quindi serve un plugin per il tuo provider DNS:

sudo apt install python3-certbot-dns-cloudflare

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
  -d miosito.it -d '*.miosito.it'

Il file delle credenziali contiene un token API e va protetto, altrimenti certbot si rifiuta di usarlo:

sudo chmod 600 /root/.secrets/cloudflare.ini

Crea un token con i permessi minimi — solo modifica delle zone DNS, solo per il dominio interessato. Se quel token finisce nelle mani sbagliate, chi lo ha può reindirizzare il tuo dominio dove vuole. Vale il discorso generale su rendere sicuro un sito.

Attenzione a un dettaglio che sfugge: *.miosito.it copre www.miosito.it e api.miosito.it, ma non copre miosito.it da solo, né a.b.miosito.it. Per questo nel comando sopra ci sono entrambi i domini.

Nota pratica: spesso il wildcard non ti serve. Se hai tre sottodomini stabili, elencarli con -d e HTTP-01 è più semplice e non richiede token API. Il wildcard ha senso quando i sottodomini nascono dinamicamente.

Gli errori che fanno perdere ore

Rate limit. Let's Encrypt limita a 5 certificati identici a settimana per lo stesso insieme di domini. Ci si arriva facilmente ripetendo certbot mentre si prova a far funzionare la configurazione: al sesto tentativo il messaggio è too many certificates already issued, e l'attesa è di una settimana intera senza scorciatoie. Usa --dry-run o --test-cert mentre stai sperimentando. L'ambiente di test non ha praticamente limiti, e i suoi certificati non sono validi per i browser — che è esattamente ciò che vuoi durante le prove.

Porta 80 chiusa. È la causa numero uno dei fallimenti con HTTP-01. Succede se il firewall blocca la 80, oppure — e questo è il caso che confonde di più — se hai "sistemato" la configurazione facendo un redirect totale da HTTP a HTTPS senza escludere il percorso della sfida. Anche il firewall del provider cloud conta, non solo quello della macchina.

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
curl -I http://miosito.it/.well-known/acme-challenge/test    # deve rispondere il tuo server

Cloudflare in modalità proxy con DNS-01 va bene, con HTTP-01 dipende. Se la nuvoletta arancione è attiva, il traffico passa da Cloudflare e la sfida HTTP-01 di solito funziona comunque, ma con la modalità SSL impostata su "Flexible" ti ritrovi cicli di reindirizzamento infiniti. Imposta "Full (strict)" una volta che il certificato è sul server.

Il rinnovo che fallisce in silenzio. È l'errore più costoso, perché non lo scopri finché il sito non è irraggiungibile con un avviso rosso a schermo intero. Cause tipiche: hai cambiato la configurazione di Nginx e il percorso della sfida non è più raggiungibile; hai spostato la webroot; hai rimosso un dominio dal DNS ma è ancora nel certificato — e in quel caso fallisce l'intero rinnovo, non solo quel dominio.

sudo certbot certificates              # cosa c'è e quando scade
sudo journalctl -u certbot.service --since "1 month ago"

Metti un controllo esterno sulla scadenza — quasi tutti i servizi di monitoraggio gratuiti hanno l'avviso sul certificato in scadenza. Un rinnovo automatico senza monitoraggio è un rinnovo automatico di cui non sai nulla.

Il servizio non ricaricato dopo il rinnovo. Certificato nuovo sul disco, vecchio in memoria, sito che scade lo stesso. Vale per i server web gestiti a mano e per qualunque applicazione che legga i certificati all'avvio — un caso frequente con i container, dove il volume viene aggiornato ma il processo dentro non lo rilegge.

Quando non ti serve certbot

Onestamente, in parecchi casi il lavoro l'ha già fatto qualcun altro.

  • Caddy ottiene e rinnova i certificati da solo, senza configurazione. Se stai partendo da zero, è il percorso più corto in assoluto — il confronto è in Nginx o Apache.
  • Traefik fa lo stesso nei contesti a container.
  • Vercel, Netlify, Cloudflare Pages e la maggior parte degli hosting gestiti gestiscono HTTPS senza che tu debba sapere che Let's Encrypt esiste.
  • Cloudflare in modalità proxy fornisce già il certificato verso il visitatore. Ti serve comunque un certificato sul server per la modalità "Full (strict)", ma lì puoi usare un Origin Certificate di Cloudflare, valido 15 anni e senza rinnovi.

Certbot serve quando gestisci tu il server. Se non lo gestisci tu, non aggiungerlo.

In sintesi

Let's Encrypt rilascia certificati HTTPS gratuiti e validi, dimostrando via software che il dominio è tuo. La durata di 90 giorni serve a rendere obbligatoria l'automazione.

HTTP-01 è il caso normale e richiede la porta 80 aperta e il dominio già puntato al server. DNS-01 è l'unico modo per i wildcard e richiede accesso API al provider DNS.

Usa fullchain.pem, mai cert.pem — è l'errore che rompe i client vecchi senza dirtelo.

Prova sempre con --dry-run o --test-cert mentre sperimenti, o finisci contro il rate limit e aspetti una settimana.

Il rinnovo automatico va verificato e monitorato, e deve ricaricare il servizio: un certificato nuovo sul disco non serve a nulla se il processo tiene in memoria quello vecchio.

Il posto dove installare tutto questo è descritto in come configurare un VPS da zero; il pezzo che sta davanti alle tue applicazioni e usa il certificato è il reverse proxy.