awen.notes
← 返回文章列表

把自签证书换成 Let's Encrypt

2026-11-08 · 约 4 分钟读完

自签证书不是不能用,只是每次换个浏览器、换台设备,都要重新经历一次“此连接不是私密连接”。自己用还能忍,给别人看就很难受了。

为什么选 webroot 方式

certbot 有几种验证方式。--nginx 插件会自动改 nginx 配置,方便但改动不可控;--webroot 只是往指定目录写一个验证文件,nginx 配置完全由自己掌握。对已经有既有配置的机器,我更倾向后者。

前提是 80 端口上能访问到 /.well-known/acme-challenge/ 这个路径。

关键点:别让 301 跳转吃掉验证请求

很多站点会把 80 全部 301 跳到 443,这在平时是好事,但 http-01 验证走的是 80 端口。如果 301 把验证请求也带走了,certbot 就会报验证失败。

正确做法是给验证路径单独开一个 location,并且放在 301 之前:

server {
    listen 80;
    server_name example.com;

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

    location / {
        return 301 https://example.com$request_uri;
    }
}

nginx 的 location 匹配有优先级,前缀匹配里更长的会赢,所以 /.well-known/acme-challenge/ 会先于 / 命中,前提是它写在这个 server 块里。

签发

certbot certonly --webroot \
  -w /var/www/html \
  -d example.com -d www.example.com

成功后证书在 /etc/letsencrypt/live/example.com/ 下,fullchain.pem 和 privkey.pem 是两个主力文件。然后改 nginx 的 ssl_certificate 指向它们,nginx -t 通过后 reload。

确认续期是自动的

Let's Encrypt 的证书 90 天过期。certbot 装好后一般会带一个 systemd timer:

systemctl list-timers | grep certbot
certbot renew --dry-run

--dry-run 一定要跑一次,它会用测试环境完整走一遍续期流程。如果不通过,说明 80 端口的验证路径哪天被改坏了,等真到期才发现就晚了。

续期之后要 reload nginx

certbot 续期只更新文件,不会自动通知 nginx 重新加载。要么用 --deploy-hook "systemctl reload nginx",要么在 timer 的 service 里配。否则证书文件是新的,nginx 内存里还是旧的,到期那天照样报错。