导读:翻翻本站过往教程,"用certbot签证书"这句话出现过太多次——反代篇提过、Compose篇提过、cron篇的自动续期示例也是它,但证书本身从来没被正面讲过。这篇补上这块拼图:HTTP验证和DNS验证怎么选、certbot的三种模式各适合谁、webroot和standalone的差别、自动续期怎么配才稳、以及"证书续期了但网站还在报警"这类经典二次翻车。全程命令可照抄,照着做一遍,你的站就从一个"网址栏带不安全字样"的站点变成挂锁站点。
一、为什么必须是HTTPS
三个硬理由:第一,浏览器对HTTP站点已经直接标"不安全",访客的第一印象就崩了;第二,没有HTTPS,表单里提交的密码在链路上是明文裸奔,公共WiFi环境下随便一个抓包工具就能看光;第三,现代Web的一堆能力——HTTP/2、Service Worker、新API——都要求HTTPS才可用。而Let’s Encrypt把证书成本降到了零:免费、全自动、三个月有效期、到期自动续。免费+自动这两个属性组合起来,意味着"没上HTTPS"在2026年已经没有任何借口。
二、开工前确认:域名和解析是前置条件
certbot申请证书时,Let’s Encrypt的验证服务器要能通过域名找到你的机器,所以先做两个检查:域名A记录已经指向这台VPS的IP,用dig或在线工具确认解析已全球生效;80端口的HTTP站点能被外网访问——HTTP验证走的就是80端口,防火墙或安全组把它挡掉,验证必失败。这一步和本站DNS篇讲过的A记录配置完全一致,还没配的同学先回去补课。
三、安装certbot并签发第一张证书
Debian/Ubuntu上安装:
apt install certbot
申请证书的命令,按你的场景三选一。场景一,机器上还没跑任何网站(或愿意临时停掉):
certbot certonly --standalone -d example.com -d www.example.com
standalone模式会自己起一个临时HTTP服务来完成验证,验证完就退出,最适合第一张证书的冷启动。场景二,网站上已经在跑Nginx,不能停:
certbot certonly --webroot -w /var/www/html -d example.com
webroot模式把验证文件写到你的网站目录里,由现有Nginx来伺服,全程不断服,是生产环境的标准姿势。场景三,证书要用在不对外提供80端口的机器上(比如内网服务、邮件服务器):
certbot certonly --manual --preferred-challenges dns -d example.com
DNS验证要求你往域名解析里加一条TXT记录证明所有权,好处是不需要任何端口可达,还能签通配符证书(-d *.example.com),代价是自动续期要配DNS API脚本。三张证书签下来,文件都落在/etc/letsencrypt/live/example.com/目录:fullchain.pem是完整证书链,privkey.pem是私钥,这两个名字后面配Nginx要用。
四、把证书挂到Nginx上
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}
顺手把80端口301跳转到https,别让两个版本并存产生重复内容。改完nginx -t做语法检查再reload——这套流程和本站反代篇完全一致,那里还有更完整的现代TLS参数配置(协议版本、加密套件、OCSP装订),这里不重复展开。挂好之后用浏览器打开,地址栏出现挂锁,第一阶段完成。
五、自动续期:cron和systemd timer两种姿势
Let’s Encrypt证书有效期90天,官方设计意图就是让你自动续。装certbot时通常自带了systemd timer:
systemctl list-timers | grep certbot
有它就什么都不用做,系统每天会检查两次,到期前30天自动续。没有timer的系统(或想自己掌控节奏),用本站cron篇的姿势加一条:
0 3 * * * certbot renew --quiet --deploy-hook "systemctl reload nginx"
renew只会真正续快到期的证书,没到期的直接跳过,所以每天跑一次毫无压力。注意那个deploy-hook:证书文件变了之后Nginx不会自动感知,必须reload才加载新证书——这就引出了本篇最重要的排坑点。
六、经典翻车一:证书续了,Nginx还在用旧的
症状:明明certbot renew输出成功,浏览器却报证书过期。原因:Nginx把旧证书读进内存后不会自动重读文件,renew换了磁盘上的证书,运行中的Nginx浑然不知。解法就是上面的deploy-hook,或者cron命令里renew后面接&& systemctl reload nginx。验证是否真的生效:
openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates
这条命令查询的是线上实际在用的证书起止日期,而不是磁盘上的文件——排查这类问题永远以它为准。同款翻车还有Docker版Nginx:容器里的Nginx看到的证书路径是挂载进来的,挂载的是整个/etc/letsencrypt目录而不是软链接目标的话,更新可能不生效,Compose篇讲过的卷挂载细节在这里会救你一命。
七、经典翻车二:验证失败的五种原因
申请或续期时报"Failed authorization",按概率排查:一,80端口没放行,防火墙、安全组、Nginx配置三层挨个查,curl http://example.com 从外网测一下通不通;二,域名解析没生效或指向了别的IP,dig example.com核对;三,webroot路径和Nginx的root不一致,验证文件写到了A目录、Nginx却在B目录找;四,前一层CDN或反代把/.well-known/acme-challenge/路径拦了或者缓存了,需要给这个路径开直通;五,之前的申请太频繁触发了Let’s Encrypt的速率限制,同一域名一周内失败验证次数有上限,撞限了只能等。前四条都能当场修,第五条教我们一个习惯:调试期用--dry-run参数,它走完整流程但用测试环境,不消耗限额。
八、通配符证书与多域名策略
要给一堆子域名上HTTPS,两条路线:通配符证书一张搞定*.example.com,但只能DNS验证,自动续期依赖域名商的API(cloudflare、阿里云都有现成的certbot插件);或者SAN多域名证书,一张证书里写example.com、www.example.com、blog.example.com,HTTP验证就行,缺点是每加一个子域名要重新签。个人站长的实用建议:主站和固定子域用SAN证书走webroot全自动,真有一堆动态子域再上通配符。证书数量没有优劣,能自动化续期的就是好证书。
结语
HTTPS的全部秘密就是:一次正确的申请、一个不会断的续期、一条reload钩子。把这三件事配好,证书就从"三个月后要记得手动续"的定时炸弹变成彻底隐形的基础设施。配合本站反代篇的TLS参数和cron篇的定时任务体系,你的网站在安全这个维度就补齐了最后一块板——浏览器地址栏那把小锁,锁住的其实是你和访客之间的整条链路。


