配置 Let's Encrypt 自动续期 HTTPS
在为域名配置 Let's Encrypt 免费 HTTPS 证书时,使用 certbot --nginx 往往能一键搞定。但在实际生产环境中,由于历史配置残留或多次修改,很容易遇到 Nginx 配置冲突导致验证失败的情况。
本文记录了从踩坑(HTTP-01 验证失败)到排错(解决 conflicting server name),最终实现证书申请与自动续期配置的完整过程。
1. 现状与痛点
- 服务器环境:Linux + Nginx
- 目标域名
example.com及www.example.com - 原有配置:域名已解析,Nginx 中已有 HTTP 80 端口及使用旧证书的 443 端口配置。
- 痛点:尝试使用 Certbot 申请证书时,报错
Challenge failed,且 Nginx 不断提示conflicting server name。
2. 核心排错:解决 Nginx 域名冲突
申请证书时报错的关键信息如下:
nginx: [warn] conflicting server name "[example.com](http://example.com/)" on 0.0.0.0:80, ignored
Detail: Invalid response from [http://example.com/.well-known/acme-challenge/](http://example.com/.well-known/acme-challenge/)...: "<!doctype html>..."
原因分析:
- Nginx 中存在多个
server块同时监听 80 端口并声明了相同的server_name,导致 Nginx 忽略了部分配置。 - Certbot 尝试访问验证文件时,请求未走到正确的验证目录,而是被错误地路由到了网站首页,返回了 HTML。
排查步骤:
使用 grep 查找所有包含该域名的配置:
sudo grep -r "[example.com](http://example.com/)" /etc/nginx/
结果发现,域名同时出现在了主配置文件和其他子配置文件中,且主配置文件内部还有重复定义。
解决方案:
合并配置,确保一个域名在同一个端口只出现一次。 删除或禁用多余的配置文件中的重复部分,只在主配置文件中保留一个结构清晰的 80 端口 server 块。
3. 正确配置 Nginx 与申请证书
3.1 规范 Nginx 配置文件
修改 Nginx 配置文件,关键是为 Certbot 的验证目录留出后门,避免被跳转规则拦截:
# ===== HTTP 80 端口配置 =====
server {
listen 80;
server_name [example.com](http://example.com/) [www.example.com](http://www.example.com/);
# 关键:放行 Certbot 验证目录,不做跳转!
location /.well-known/acme-challenge/ {
root /usr/share/nginx/html; # 确保此目录存在,或替换为你实际的网站根目录
}
# 其他所有 HTTP 请求,跳转到 HTTPS(推荐使用 return 301)
location / {
return 301 [https://$host$request_uri](https://$host$request_uri/);
}
}
# ===== HTTPS 443 端口配置 =====
server {
listen 443 ssl;
server_name [example.com](http://example.com/) [www.example.com](http://www.example.com/);
# 暂时保留旧证书路径,等申请成功后再替换
ssl_certificate /path/to/your/old_cert.crt;
ssl_certificate_key /path/to/your/old_cert.key;
# ... 其他网站业务配置 ...
}
💡 踩坑提示:HTTP 跳转 HTTPS 强烈建议使用
return 301 https://$host$request_uri;而非rewrite ^(.*) https://$server_name$1 permanent;。前者性能更好,且能完美保留原始域名(www 与裸域)及查询参数。
测试并重载 Nginx,确保不再出现警告:
sudo nginx -t && sudo systemctl reload nginx
3.2 执行 Certbot 申请证书
运行以下命令申请纯证书(使用 certonly 模式,不让 Certbot 乱动我们的 Nginx 配置):
sudo certbot certonly --nginx -d [example.com](http://example.com/) -d [www.example.com](http://www.example.com/)
此时验证将顺利通过,输出成功信息及证书路径:
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
Key is saved at: /etc/letsencrypt/live/example.com/privkey.pem
3.3 替换 Nginx 证书路径
再次编辑 Nginx 配置,将 443 端口块的证书路径替换为 Let's Encrypt 签发的新路径:
server {
listen 443 ssl;
server_name [example.com](http://example.com/) [www.example.com](http://www.example.com/);
# 替换为 Let's Encrypt 证书路径
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# ...
}
重载 Nginx 生效:
sudo nginx -t && sudo systemctl reload nginx
4. 关键一步:配置自动续期与重载
Let's Encrypt 证书有效期仅 90 天。虽然 Certbot 自带了定时任务来检查续期,但使用 certonly 模式时,续期后 Nginx 不会自动重载,会导致旧证书仍在内存中生效,最终过期。
我们需要添加一个 deploy_hook,让续期成功后自动重载 Nginx。
编辑续期配置文件:
sudo nano /etc/letsencrypt/renewal/[example.com](http://example.com/).conf
在文件末尾添加一行:
deploy_hook = systemctl reload nginx
(保存退出:Ctrl+O -> Enter -> Ctrl+X)
模拟自动续期测试:
sudo certbot renew --dry-run
若输出以下关键信息,则大功告成:
new certificate deployed with reload of nginx server; fullchain is...
Congratulations, all renewals succeeded.
5. 总结
- 遇到
conflicting server name必须清理 Nginx 重复配置,这是 HTTP-01 验证失败的万恶之源。 - 80 端口配置必须优先放行
.well-known/acme-challenge/目录。 - 推荐使用
return 301进行跳转。 - 使用
certonly模式时,务必手动配置deploy_hook以实现证书续期后自动重载 Nginx,否则自动化是不完整的。
按照以上流程,即可一劳永逸地解决 Let's Encrypt 证书的申请与自动续期问题。