Web Services

HTTPS Certificates and Let's Encrypt Auto-Renewal: From Issuance to a cron Fallback

Manually requesting a cert is easy; the hard part is renewing it before expiry automatically. This guide walks certbot through webroot and HTTP-01 validation, configures renewal on port 80, and pairs it with two fallbacks so missing a renewal still does not take the site down.

By LaoHand Team·7 min read·Updated 2026-09-06

Why automate: the 90-day lifetime forces you to master the flow

Let's Encrypt pushes certificate lifetime down to 90 days not to annoy you but to force you to automate and avoid the classic accident of an old long-lived cert expiring unnoticed. Ninety days means renewing four times a year; remembering by hand almost certainly fails, so automation is the only thing keeping the site up.

The renewal works by reissuing: certbot renew reads every cert due within the next 30 days and walks a fresh issuance to swap in a new one. Anything still comfortably valid is skipped, so running renew weekly is entirely safe.

certbot --version
sudo certbot certificates
# 干跑一遍,看会不会重签
sudo certbot renew --dry-run
# 看每张证的到期时间
sudo certbot certificates | grep -E "Expiry Date|Certificate Name"

Choosing the issuance method: two reasons webroot beats standalone

standalone temporarily binds port 80 and answers validation itself, so if nginx owns 80 or a load balancer sits in front, it either collides or never gets validated correctly. webroot writes a token file into a directory of your choice and lets the already-running web server answer it, changing the least.

For most nginx deployments webroot is the robust default; only when the reverse proxy already routes away / and the acme directory should you consider DNS-01. Its precondition is that nginx points /.well-known/acme-challenge/ at our directory—that is handled as the first step below.

# nginx: 让 ACME 校验流量落在 webroot
location /.well-known/acme-challenge/ {
    root /var/www/certbot;
    default_type text/plain;
}
# 目录与权限
sudo mkdir -p /var/www/certbot/.well-known
sudo chown www-data:www-data /var/www/certbot -R
sudo nginx -t && sudo systemctl reload nginx

First issuance: point webroot and the domain combo

The key to the issuance command is pairing -w (webroot path) with -d (domain); when one cert spans multiple domains, attach each domain with its own root path, or certbot warns with a "requested /path was not..." style error.

Also nail down the chain: issuance yields fullchain.pem and privkey.pem as the two critical artifacts, which nginx ssl_certificate and ssl_certificate_key point at respectively. Smoke-test https right after generation instead of discovering a broken chain only after config.

sudo certbot certonly --webroot -w /var/www/certbot \
    -d example.com -d www.example.com \
    -m admin@example.com --agree-tos --no-eff-email
# 校验产物存在
sudo ls -l /etc/letsencrypt/live/example.com/
# 立即冒烟
curl -sI https://example.com -o /dev/null -w "%{http_code}\n"

Wire auto-renewal: certbot.timer plus a cron fallback

The certbot package ships a systemd renew.timer that fires twice a day; on most distros it is enabled out of the box, so verify it is active. If your distro lacks the timer, or you want a fixed schedule that also reloads Nginx, add a cron entry on top.

The deploy hook is an unsung killer: run a reload automatically after a successful renewal so the server never keeps serving the old cert. Below are three parts: checking the timer, a cron line, and the deploy hook.

# 系统自带 timer 是否启用
sudo systemctl status certbot.timer
# 兜底 cron:每周一凌晨 2:30 续期并 reload
30 2 * * 1 certbot renew --quiet --deploy-hook "systemctl reload nginx"
# deploy 钩子示例(放到同目录):
# /etc/letsencrypt/renewal-hooks/deploy/reload-nginx
systemctl reload nginx
# 验证续期链路
sudo certbot renew --dry-run

Mistake vs fix: renewal config pointing at the wrong webroot

A very common trap: the first issuance uses -w to point at the right webroot, later the directory is moved for some reason, but the renewal config keeps the old webroot—so at renewal the ACME challenge 404s and the cert quietly rides toward expiry.

Do not hand-edit the path by heart in /etc/letsencrypt renewal files. Instead read the renewal conf for that cert and correct the installer/webroot fields, or just run certonly again so certbot rewrites it. Then --dry-run to prove the path is right.

# 错误示范:改了目录没同步 renewal 配置
# apt move /var/www/certbot /srv/acme   # 然后忘了改 webroot

# 修复对照:查看并校正 renewal 配置
sudo cat /etc/letsencrypt/renewal/example.com.conf | grep -i webroot
# 重新签发以重写配置
sudo certbot certonly --webroot -w /srv/acme -d example.com --force-renewal
sudo certbot renew --dry-run

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.