Certificati SSL e backup che falliscono in silenzio: come farsi avvisare
I due guasti che fanno più danni su un server di hosting sono anche quelli che non fanno rumore. Un certificato che non si rinnova funziona perfettamente fino al giorno in cui scade, e allora il sito mostra un errore di sicurezza a tutti i visitatori. Un backup che smette di funzionare non se ne accorge nessuno, finché non serve. La regola è semplice: non fidarti del fatto che una cosa automatica funzioni, controlla il risultato. Per i certificati guarda quanti giorni mancano alla scadenza, non se certbot è installato; per i backup guarda quando è stato fatto l'ultimo backup riuscito, non se il cron esiste. Qui trovi i controlli pronti da copiare.
Perché nessuno ti avvisa più
Fino al 2025 Let's Encrypt mandava un'email quando un certificato stava per scadere senza essere stato rinnovato. Quel servizio è stato chiuso: oggi, se il rinnovo automatico si rompe, l'unico avviso che ricevi è quello del cliente che non riesce ad aprire il sito. In più Let's Encrypt ha annunciato che la durata dei certificati scenderà gradualmente da 90 a 45 giorni entro il 2028: i rinnovi saranno più frequenti e un rinnovo rotto farà danni prima.
Per i backup è peggio: nessun programma di backup ti scrive «stanotte non sono partito». Se il cron sparisce dopo una migrazione, se la password dello storage cambia o se il disco di destinazione si riempie, il backup semplicemente non c'è.
1. Quanti giorni mancano alla scadenza di un certificato
Il controllo giusto è dall'esterno, come lo vede un browser:
echo | openssl s_client -servername www.example.com -connect www.example.com:443 2>/dev/null \
| openssl x509 -noout -enddate
La risposta è una data come notAfter=Dec 24 10:15:02 2026 GMT. Per sapere se scade entro 14 giorni, openssl ha un'opzione apposta che restituisce un codice di uscita:
echo | openssl s_client -servername www.example.com -connect www.example.com:443 2>/dev/null \
| openssl x509 -noout -checkend $((14*86400)) || echo "SCADE ENTRO 14 GIORNI"
Perché 14 giorni? Certbot prova a rinnovare circa 30 giorni prima della scadenza. Se a 14 giorni il certificato è ancora il vecchio, il rinnovo non funziona da più di due settimane e hai ancora il tempo di sistemarlo.
2. Un controllo per tutti i siti, ogni giorno
Metti l'elenco dei domini in un file e controllali tutti con uno script:
#!/bin/bash
# /usr/local/bin/controlla-certificati
GIORNI=14
while read -r dominio; do
[ -z "$dominio" ] && continue
fine=$(echo | timeout 10 openssl s_client -servername "$dominio" -connect "$dominio:443" 2>/dev/null \
| openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
if [ -z "$fine" ]; then
echo "$dominio: nessun certificato leggibile"
continue
fi
restano=$(( ($(date -d "$fine" +%s) - $(date +%s)) / 86400 ))
[ "$restano" -lt "$GIORNI" ] && echo "$dominio: scade tra $restano giorni ($fine)"
done < /etc/domini-da-controllare.txt
Con cron, l'output di un comando viene mandato per email all'utente se il server sa spedire posta. Una riga in crontab -e basta:
MAILTO=tu@example.com
30 7 * * * /usr/local/bin/controlla-certificati
Lo script non stampa niente se va tutto bene, quindi ricevi un'email solo quando c'è un problema.
3. Capire perché certbot non rinnova
Quando un certificato non si rinnova, i controlli sono sempre gli stessi:
systemctl list-timers | grep -i certbot # il timer esiste ed è attivo?
sudo certbot renew --dry-run # il rinnovo funzionerebbe adesso?
sudo tail -50 /var/log/letsencrypt/letsencrypt.log
Le cause più frequenti sono tre: il dominio non punta più a questo server (il cliente ha cambiato DNS), la porta 80 è chiusa dal firewall o da un CDN davanti al sito, oppure la configurazione nginx del sito è stata modificata e la verifica /.well-known/acme-challenge/ non arriva più dove certbot se l'aspetta.
4. Backup: controlla l'età dell'ultimo backup riuscito
Il controllo utile non è «il backup è partito» ma «quando è finito l'ultimo backup riuscito». Con restic:
restic snapshots --latest 1 --json | jq -r '.[-1].time'
E un controllo che avvisa se l'ultimo backup ha più di 26 ore (un giorno più un margine):
#!/bin/bash
# /usr/local/bin/controlla-backup (RESTIC_REPOSITORY e RESTIC_PASSWORD_FILE nell'ambiente)
ultimo=$(restic snapshots --latest 1 --json | jq -r '.[-1].time // empty')
if [ -z "$ultimo" ]; then echo "Nessun backup nel repository"; exit 1; fi
ore=$(( ($(date +%s) - $(date -d "$ultimo" +%s)) / 3600 ))
[ "$ore" -gt 26 ] && echo "Ultimo backup riuscito $ore ore fa ($ultimo)"
Una volta alla settimana verifica anche che i dati nel repository siano leggibili, controllandone una parte a rotazione:
restic check --read-data-subset=5%
La guida completa per impostare i backup con restic su uno storage S3 è in backup dei siti su S3.
5. Il «dead man's switch»: l'avviso quando qualcosa NON succede
Gli script di controllo girano sullo stesso server dei backup: se il server si spegne, non ti avvisano. Il rimedio è un servizio esterno che aspetta un segnale periodico e ti avvisa quando il segnale non arriva. Servizi come Healthchecks.io funzionano così: aggiungi una chiamata alla fine del backup, solo se è riuscito.
restic backup /var/www /var/backups/mysql && curl -fsS -m 10 --retry 3 https://hc-ping.com/IL-TUO-UUID
Se per 26 ore il segnale non arriva (backup fallito, cron sparito, server spento), ricevi l'avviso. È il controllo più robusto di tutti, perché non dipende dal server che controlla.
6. L'unica prova vera: ripristinare
Un backup che non hai mai ripristinato è un'ipotesi. Una volta al mese ripristina un sito a caso in una cartella temporanea e verifica che file e database ci siano:
restic restore latest --target /tmp/prova-ripristino --include /var/www/example.com
ls -la /tmp/prova-ripristino/var/www/example.com
Poi cancella la cartella. Dieci minuti al mese, e sai che quando servirà funzionerà.
Con Prometheus
Se usi già Prometheus, i certificati li controlla blackbox_exporter con la metrica probe_ssl_earliest_cert_expiry, e i backup puoi controllarli esponendo l'età dell'ultimo snapshot con il textfile collector di node_exporter. Lo spieghiamo in monitorare un server hosting con Prometheus e Grafana.
Cosa fa Koapanel
Su un server con Koapanel questi controlli ci sono già:
- Certificato in scadenza: se a meno di 14 giorni dalla scadenza il rinnovo automatico non è riuscito, gli amministratori ricevono un'email con il sito interessato.
- Backup non riuscito: un'email per ogni backup fallito, e l'ultimo backup riuscito di ogni sito si vede nella lista di tutti i siti.
- Disco quasi pieno: un'email oltre il 90%.
- Lo stesso avviso arriva al massimo una volta al giorno, così la casella non si riempie (Email di sistema).
Dalla versione 0.27 le stesse informazioni sono anche metriche per Prometheus e Grafana, compresa la regola «nessun backup riuscito da due giorni» per ogni sito (API e webhook).
Quello che resta a te: un controllo esterno di disponibilità (il dead man's switch o un servizio di uptime) e la prova di ripristino ogni tanto. Il pannello ripristina un sito con un clic dalla pagina Backup, quindi la prova richiede un minuto.
Domande frequenti
Ogni quanto controllare i certificati?
Una volta al giorno basta. Con una soglia di 14 giorni hai tredici controlli prima della scadenza.
Il certificato risulta valido dal server ma il browser dice che è scaduto: com'è possibile?
Di solito c'è un CDN o un proxy davanti al sito che usa un suo certificato, oppure il dominio punta a un altro server. Per questo il controllo va fatto dall'esterno, con openssl s_client sul nome del sito, non leggendo il file del certificato sul disco.
Bastano i backup del provider del VPS?
Sono utili contro la perdita del server intero, ma di solito sono giornalieri, conservati per pochi giorni e ripristinano tutto il server. Per ripristinare un solo sito o un database di ieri serve un backup tuo, per sito, fuori dal server.
Quanto spesso provare un ripristino?
Almeno una volta al mese e dopo ogni cambio importante (nuovo storage, nuova password, migrazione).
Prova Koapanel
Guarda il pannello nella demo pubblica oppure installalo su un Ubuntu 24.04 appena creato, gratis fino a 3 siti:
curl -fsSL https://get.koapanel.app | sudo bash