主题
SSL 证书从 TrustAsia 切 Let's Encrypt:四站点迁移全记录
一句话总结:从付费 TrustAsia 证书迁移到免费 Let's Encrypt 证书,踩了 webroot 验证路径被代理和 .well-known 访问不到的坑,最终用
certbot certonly --webroot逐个攻破四站点。
| 字段 | 内容 |
|---|---|
| 标签 | #SSL #Let's-Encrypt #certbot #nginx #证书迁移 |
| 踩坑日期 | 2026-07-19 |
| 严重程度 | 💀 高(影响线上 HTTPS 服务) |
事故经过
背景
服务器 124.222.126.171 上有四个站点需要 SSL 证书:
| 站点 | 域名 | 后端端口 |
|---|---|---|
| 小雅 APP | www.sirenzhuli.cn | 8898 |
| 主站聊天 | chat.sirenzhuli.cn | 8898 |
| 文件服务 | files.sirenzhuli.cn | 8898 |
| 主站(根域) | sirenzhuli.cn | 80 |
之前用的 TrustAsia 付费证书快过期了,风哥说切 Let's Encrypt 免费证书,省下的钱买杨枝甘露。
时间线
第一次尝试:直接用
certbot --nginx全自动模式。结果 certbot 自动改了 nginx 配置,但改乱了已有配置,差点把现有站点搞崩。紧急回滚。第二次尝试:改用
certbot certonly --standalone独立模式。但这要求 80/443 端口空闲用于验证,而 nginx 正占着这些端口。失败了。第三次尝试:改用
certbot certonly --webroot模式。原理是在站点根目录下放一个验证文件,Let's Encrypt 服务器通过 HTTP 访问这个文件来验证域名所有权。翻车:chat.sirenzhuli.cn 的验证老是失败:
Invalid response from http://chat.sirenzhuli.cn/.well-known/acme-challenge/xxx返回 404。但 www.sirenzhuli.cn 的验证是正常的!
排查发现:chat 站点的 nginx 配置把所有请求(包括
/.well-known/)都proxy_pass到了后端 8898 端口,而后端根本没有.well-known目录,所以返回 404。逐一解决:为每个站点的 nginx 配置添加
.well-known的静态文件处理规则,绕过后端代理。
根因分析
证书验证的原理
Let's Encrypt 的 HTTP-01 验证流程:
1. certbot 在本地生成一个随机 token 文件
→ /var/www/xxx/.well-known/acme-challenge/<random-token>
2. certbot 告诉 Let's Encrypt 服务器:"去访问这个 URL"
3. Let's Encrypt 服务器发起 HTTP 请求:
→ http://chat.sirenzhuli.cn/.well-known/acme-challenge/<random-token>
4. 如果返回的内容正确 → 验证通过 → 签发证书为什么 chat 站点失败
nginx 配置块里:
nginx
# ❌ 错误的配置:所有请求都代理到后端
location / {
proxy_pass http://127.0.0.1:8898;
}这导致任何路径的请求——包括 /.well-known/ ——都被转发给了后端服务。后端 Python 程序不认识这个路径,返回 404。
而 www.sirenzhuli.cn 恰好配了静态文件目录,所以能正常工作。
关键认知
location / 是一个"兜底规则"——它匹配所有路径。如果你不显式地为 /.well-known/ 写一条更具体的 location,那么 location / 就会"吃掉"所有请求。
nginx 的 location 匹配优先级:
= 精确匹配 > ^~ 前缀匹配 > ~ 正则匹配 > 普通前缀匹配(最长优先)location /.well-known/ 是一个普通前缀匹配,但它比 location / 更长,所以优先级更高。
解决方案
Step 1:安装 certbot
bash
# Ubuntu/Debian
sudo apt update
sudo apt install certbot python3-certbot-nginx -y
# 验证安装
certbot --versionStep 2:为每个站点准备 webroot 目录
bash
# 创建统一的 webroot 目录
sudo mkdir -p /var/www/certbot/.well-known/acme-challenge
sudo chown -R www-data:www-data /var/www/certbot/Step 3:修改每个站点的 nginx 配置
核心:在所有 location / 之前,先添加 /.well-known/ 的静态处理规则!
nginx
server {
listen 80;
server_name chat.sirenzhuli.cn;
# ✅ 关键:优先匹配 .well-known,不走代理
location ^~ /.well-known/acme-challenge/ {
root /var/www/certbot;
default_type text/plain;
}
# 其他请求走代理
location / {
proxy_pass http://127.0.0.1:8898;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}⚠️ 用
^~前缀可以确保这个规则有最高优先级,不会被后面的正则规则覆盖。
Step 4:逐个签发证书
bash
# 站点1:www.sirenzhuli.cn
sudo certbot certonly --webroot \
-w /var/www/certbot \
-d www.sirenzhuli.cn \
--email your-email@example.com \
--agree-tos \
--non-interactive
# 站点2:chat.sirenzhuli.cn
sudo certbot certonly --webroot \
-w /var/www/certbot \
-d chat.sirenzhuli.cn
# 站点3:files.sirenzhuli.cn
sudo certbot certonly --webroot \
-w /var/www/certbot \
-d files.sirenzhuli.cn
# 站点4:sirenzhuli.cn(裸域)
sudo certbot certonly --webroot \
-w /var/www/certbot \
-d sirenzhuli.cn也可以一次申请多个域名的证书(SAN 证书):
bashsudo certbot certonly --webroot \ -w /var/www/certbot \ -d www.sirenzhuli.cn \ -d chat.sirenzhuli.cn \ -d files.sirenzhuli.cn \ -d sirenzhuli.cn
Step 5:更新 nginx 配置使用新证书
nginx
server {
listen 443 ssl http2;
server_name chat.sirenzhuli.cn;
# Let's Encrypt 证书路径
ssl_certificate /etc/letsencrypt/live/chat.sirenzhuli.cn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/chat.sirenzhuli.cn/privkey.pem;
# SSL 安全配置(推荐)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# 验证路由(续期时还会用到)
location ^~ /.well-known/acme-challenge/ {
root /var/www/certbot;
default_type text/plain;
}
location / {
proxy_pass http://127.0.0.1:8898;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# HTTP 强制跳转 HTTPS
server {
listen 80;
server_name chat.sirenzhuli.cn;
return 301 https://$server_name$request_uri;
}Step 6:验证 + 重载
bash
# 语法检查
sudo nginx -t
# 重载
sudo systemctl reload nginx
# 验证证书
echo | openssl s_client -servername chat.sirenzhuli.cn -connect chat.sirenzhuli.cn:443 2>/dev/null | openssl x509 -noout -dates预期输出:
notBefore=Jul 19 00:00:00 2026 GMT
notAfter=Oct 17 00:00:00 2026 GMTStep 7:配置自动续期
Let's Encrypt 证书有效期只有 90 天,必须配置自动续期。
bash
# certbot 安装时已自动注册 systemd timer,检查一下
sudo systemctl status certbot.timer
# 手动测试续期(--dry-run 不会真的续期,只验证流程)
sudo certbot renew --dry-run续期时的 nginx 配置必须保留 /.well-known/ 的静态路由,否则续期验证会失败。
预防措施
新站点上架 SSL 检查清单
风哥定的新站点上架清单(2026-08-07):
1. □ SSL 证书:用 certbot certonly --webroot 签 Let's Encrypt 免费证书
2. □ nginx 配置中必须保留 .well-known 静态路由
3. □ 备案号放在底部
4. □ 域名全部用 xxx.sirenzhuli.cn 子域名,A 记录 → 124.222.126.171证书管理 SOP
bash
# 查看所有证书及其过期时间
sudo certbot certificates
# 查看续期 timer 状态
sudo systemctl list-timers | grep certbot
# 证书过期前 30 天会自动尝试续期,但最好每月手动检查一次
sudo certbot renew --dry-run故障恢复
如果自动续期失败导致证书过期,紧急操作:
bash
# 1. 立即重新签发
sudo certbot certonly --webroot -w /var/www/certbot -d xxx.sirenzhuli.cn --force-renewal
# 2. 重载 nginx
sudo nginx -t && sudo systemctl reload nginx
# 3. 验证
echo | openssl s_client -servername xxx.sirenzhuli.cn -connect xxx.sirenzhuli.cn:443 2>/dev/null | openssl x509 -noout -dates关联知识点
- SSL/TLS 基础知识 — 证书链、TLS 握手、HSTS
- nginx 配置不生效排故 — sites-available vs sites-enabled
- Linux 进程管理 — systemctl reload vs restart
- 网络安全基础概念 — HTTP 和 HTTPS 的区别
- 防火墙配置 — 确认 80/443 端口开放
加载练习题中...
🎯 本章要点
- Let's Encrypt 免费 + certbot 自动续期,安全级别与付费证书相同
- certbot certonly --webroot 模式不自动改 nginx 配置,适合生产环境