Skip to content

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 证书:

站点域名后端端口
小雅 APPwww.sirenzhuli.cn8898
主站聊天chat.sirenzhuli.cn8898
文件服务files.sirenzhuli.cn8898
主站(根域)sirenzhuli.cn80

之前用的 TrustAsia 付费证书快过期了,风哥说切 Let's Encrypt 免费证书,省下的钱买杨枝甘露。

时间线 ​

  1. 第一次尝试:直接用 certbot --nginx 全自动模式。结果 certbot 自动改了 nginx 配置,但改乱了已有配置,差点把现有站点搞崩。紧急回滚。

  2. 第二次尝试:改用 certbot certonly --standalone 独立模式。但这要求 80/443 端口空闲用于验证,而 nginx 正占着这些端口。失败了。

  3. 第三次尝试:改用 certbot certonly --webroot 模式。原理是在站点根目录下放一个验证文件,Let's Encrypt 服务器通过 HTTP 访问这个文件来验证域名所有权。

  4. 翻车:chat.sirenzhuli.cn 的验证老是失败:

    Invalid response from http://chat.sirenzhuli.cn/.well-known/acme-challenge/xxx

    返回 404。但 www.sirenzhuli.cn 的验证是正常的!

  5. 排查发现:chat 站点的 nginx 配置把所有请求(包括 /.well-known/)都 proxy_pass 到了后端 8898 端口,而后端根本没有 .well-known 目录,所以返回 404。

  6. 逐一解决:为每个站点的 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 --version

Step 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 证书):

bash
sudo 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 GMT

Step 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

关联知识点 ​

加载练习题中...

🎯 本章要点 ​

  • Let's Encrypt 免费 + certbot 自动续期,安全级别与付费证书相同
  • certbot certonly --webroot 模式不自动改 nginx 配置,适合生产环境