网站还在用 http,浏览器地址栏会标“不安全”,部分支付、小程序和 API 接口也强制要求 HTTPS。买商业证书一年几百到几千,其实个人站和小团队完全可以用 Let's Encrypt 免费证书:90 天有效期、浏览器和系统信任、配合 Certbot 可以自动续期,配好之后基本不用再管。这篇按“申请—配置—续期—排查”的顺序,把 Nginx + Certbot 的完整流程讲一遍。
一、申请前先确认三件事
- 域名已解析到这台服务器,A / AAAA 记录已经生效。
- 服务器 80 和 443 端口对公网开放,安全组和本机防火墙都要放行。
- Nginx 已安装,并且能用域名正常访问 HTTP 页面。
如果域名套了 CDN,要确认回源能访问到验证文件。Cloudflare 开启代理时,HTTP-01 验证可能失败,可以临时关闭代理,或者改用 DNS-01 验证。
二、安装 Certbot
Debian / Ubuntu 直接装:
apt update apt install -y certbot python3-certbot-nginx
CentOS / RHEL 需要先启用 EPEL:
dnf install -y epel-release dnf install -y certbot python3-certbot-nginx
装完确认版本:
certbot --version
三、申请证书
最省事的方式是让 Certbot 自动修改 Nginx 配置:
certbot --nginx -d example.com -d www.example.com
按提示填写邮箱、同意协议,并选择是否强制跳转 HTTPS。执行完成后 Certbot 会自动写入证书路径并 reload Nginx。
如果只想签发证书、不改动现有配置:
certbot certonly --nginx -d example.com -d www.example.com
证书默认放在 /etc/letsencrypt/live/example.com/ 目录,Nginx 需要用到 fullchain.pem 和 privkey.pem 两个文件。
四、手动配置 Nginx HTTPS
没有使用 --nginx 自动配置的话,可以参考下面的写法。先让 HTTP 正常跳转,同时保留 ACME 验证目录:
server {
listen 80;
server_name example.com www.example.com;
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://$host$request_uri;
}
}
再配置 443 端口:
server {
listen 443 ssl;
http2 on; # Nginx 1.25.1 及以上;更早版本写成 listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
add_header Strict-Transport-Security "max-age=31536000" always;
# 这里放业务配置
}
注意 http2 on; 是 Nginx 1.25.1 之后新增的写法,老版本用 listen 443 ssl http2;,两种不要同时写。改完先检查再重载:
nginx -t && systemctl reload nginx
五、验证是否生效
curl -I https://example.com
返回 HTTP/2 200 并带上证书信息,说明基本配置正确。再用浏览器确认锁标志,用 SSL Labs 或 myssl 检测评分。http 跳转、www 与非 www 两个域名都要各点一遍。
六、自动续期
Certbot 安装时会注册 systemd timer 或 cron,先确认定时任务存在:
systemctl status certbot.timer
再跑一次模拟续期:
certbot renew --dry-run
dry-run 成功就说明续期链路没问题。证书剩余 30 天时,timer 会自动续期。建议加上 deploy hook,让续期完成后自动重载 Nginx:
certbot renew --deploy-hook "systemctl reload nginx"
也可以把重载脚本放到 /etc/letsencrypt/renewal-hooks/deploy/ 目录,所有续期都会自动执行。
七、常见问题
- 80 端口被占用:先停掉占用程序,或改用 DNS-01 验证。
- 验证返回 404:确认 /.well-known/acme-challenge/ 能公网访问,CDN 没有拦截。
- 触发签发频率限制:Let's Encrypt 对同一组域名的重复证书有每周次数限制,调试时加 --staging,正式环境再换回来。
- 证书链不完整:Nginx 要配置 fullchain.pem,不要只配 cert.pem。
- 续期后没生效:确认 deploy hook 里执行了 nginx -t 和 reload。
- 域名太多:把 -d 参数都写上,或者拆成多张证书,不要用一张证书硬塞几十个不相关域名。
结语
免费证书不等于低质量。Let's Encrypt 的信任链和商业 DV 证书没有本质区别,关键是把自动续期和 reload 做对。装一次、跑一次 dry-run,后面基本可以忘了这件事。