强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Certbot 证书自动化教程 / 第 10 章:多域名与通配符证书

第 10 章:多域名与通配符证书

10.1 证书类型概述

在实际运维中,一个域名对应一张证书往往不是最优方案。理解不同证书类型的适用场景,能有效降低管理复杂度。

证书覆盖范围对比

证书类型覆盖范围示例验证方式
单域名证书一个域名example.comHTTP-01 / DNS-01
SAN 证书多个独立域名example.com, blog.comHTTP-01 / DNS-01
通配符证书所有同级子域名*.example.com仅 DNS-01
SAN + 通配符根域名 + 通配符example.com, *.example.com仅 DNS-01

SAN(Subject Alternative Name)简介

SAN 是 X.509 证书中的扩展字段,允许一张证书包含多个域名标识(DNS 名称)。Let’s Encrypt 签发的所有证书都是 SAN 证书,单个证书最多支持 100 个域名标识

# 查看证书中的 SAN 信息
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -text | grep "DNS:"
# 输出示例:
# DNS:example.com, DNS:www.example.com, DNS:api.example.com

10.2 多域名证书(SAN 证书)

申请多域名证书

# 方式一:单个 -d 参数列出所有域名
sudo certbot certonly --nginx \
  -d example.com \
  -d www.example.com \
  -d api.example.com \
  -d admin.example.com

# 方式二:合并为一个 -d 参数(逗号分隔)
sudo certbot certonly --nginx \
  -d example.com,www.example.com,api.example.com,admin.example.com

# 使用 webroot 模式
sudo certbot certonly --webroot -w /var/www/certbot \
  -d example.com -d www.example.com -d api.example.com

# 使用 DNS 验证
sudo certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare/credentials.ini \
  -d example.com -d www.example.com -d api.example.com

跨域名 SAN 证书

# 一张证书覆盖多个完全不同的域名
sudo certbot certonly --webroot -w /var/www/certbot \
  -d site1.com \
  -d site2.com \
  -d site3.org

注意: 所有域名必须都能通过相同的验证方式完成域名验证。使用 webroot 模式时,所有域名需要指向同一个 Web 根目录。

不同 Webroot 的多域名

# 每个域名使用不同的 webroot 路径
sudo certbot certonly \
  --webroot -w /var/www/site1 -d site1.com \
  --webroot -w /var/www/site2 -d site2.com

10.3 通配符证书

通配符证书覆盖范围

证书域名匹配不匹配
*.example.comwww.example.comexample.com
api.example.comsub.www.example.com
mail.example.comexample.org
anything.example.com

申请通配符证书

# 必须使用 DNS-01 验证
# Cloudflare 插件
sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare/credentials.ini \
  -d example.com \
  -d "*.example.com" \
  --agree-tos \
  --email [email protected]

# Route53 插件
sudo certbot certonly \
  --dns-route53 \
  -d example.com \
  -d "*.example.com" \
  --agree-tos \
  --email [email protected]

# 手动 DNS 验证
sudo certbot certonly --manual \
  --preferred-challenges dns \
  -d example.com \
  -d "*.example.com"

通配符 + 根域名

# 推荐:同时包含根域名和通配符
sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare/credentials.ini \
  -d example.com \
  -d "*.example.com"

生成的证书同时适用于 example.com 和所有 *.example.com 子域名。

多级通配符不支持

# ❌ 以下写法无效
-d "*.sub.example.com"   # 仅匹配 sub.example.com 的下一级
-d "*.*.example.com"     # 不支持多个通配符层级

# ✅ 需要多个通配符证书
-d "*.example.com"       # 匹配 xxx.example.com
-d "*.sub.example.com"   # 匹配 xxx.sub.example.com

10.4 多站点管理策略

策略一:每个站点一张证书

site1.com          → 证书 1 (site1.com)
site2.com          → 证书 2 (site2.com)
site3.com          → 证书 3 (site3.com)
sudo certbot certonly --nginx -d site1.com
sudo certbot certonly --nginx -d site2.com
sudo certbot certonly --nginx -d site3.com

优点: 隔离性好,续期互不影响 缺点: 证书数量多,管理开销大

策略二:合并为多域名证书

site1.com + site2.com + site3.com → 一张 SAN 证书
sudo certbot certonly --nginx \
  -d site1.com -d site2.com -d site3.com

优点: 减少证书数量 缺点: 所有站点共用一张证书,续期时全部受影响

策略三:通配符 + 根域名

example.com + *.example.com → 一张通配符证书
sudo certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare/credentials.ini \
  -d example.com -d "*.example.com"

优点: 一张证书覆盖所有子域名 缺点: 仅限同级子域名,需要 DNS 验证

策略四:混合方案

example.com + *.example.com     → 通配符证书 1
blog.com + www.blog.com         → 证书 2
api.partner.com                 → 证书 3

策略选择指南

场景推荐策略理由
单个独立站点每站一证简单清晰
同一主域下多个子域通配符证书管理简单
多个不同域名SAN 证书减少证书数量
多个不同域+子域混合方案灵活高效
微服务架构通配符 + SAN覆盖全面

10.5 向现有证书添加域名

# 使用 --expand 参数向现有证书添加域名
sudo certbot --nginx --cert-name example.com \
  -d example.com \
  -d www.example.com \
  -d api.example.com

–expand 与 –expand 关键区别

参数行为
无 –expand如果证书名已存在,会报错
--expand向现有证书添加新域名,替换旧证书

实际操作

# 查看当前证书
sudo certbot certificates

# 原证书包含: example.com, www.example.com
# 添加 api.example.com
sudo certbot --nginx --cert-name example.com \
  -d example.com \
  -d www.example.com \
  -d api.example.com

# 验证
sudo certbot certificates
# 现在包含: example.com, www.example.com, api.example.com

10.6 证书管理命令

列出所有证书

sudo certbot certificates

输出示例:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Found the following certs:
  Certificate Name: example.com
    Serial Number: abcdef123456
    Key Type: ECDSA
    Domains: example.com www.example.com api.example.com
    Expiry Date: 2025-08-10 12:00:00+00:00 (VALID: 80 days)
    Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem
    Private Key Path: /etc/letsencrypt/live/example.com/privkey.pem

  Certificate Name: blog.example.com
    Serial Number: 7890abcdef12
    Key Type: ECDSA
    Domains: blog.example.com
    Expiry Date: 2025-07-15 06:00:00+00:00 (VALID: 55 days)
    Certificate Path: /etc/letsencrypt/live/blog.example.com/fullchain.pem
    Private Key Path: /etc/letsencrypt/live/blog.example.com/privkey.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

证书重命名

# 重命名证书
sudo certbot certrename --cert-name old-name --new-name new-name

删除证书

# 删除证书
sudo certbot delete --cert-name example.com

# 交互式删除
sudo certbot delete

撤销证书

# 撤销证书(向 CA 发送撤销请求)
sudo certbot revoke --cert-name example.com

# 撤销并删除
sudo certbot revoke --cert-name example.com --delete-after-revoke

# 撤销原因
sudo certbot revoke --cert-name example.com --reason keyCompromise

10.7 多域名 Nginx 配置

通配符证书 Nginx 配置

# 通用 SSL 参数
# /etc/nginx/snippets/ssl-params.conf
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
# 主站
# /etc/nginx/sites-available/example.com.conf
server {
    listen 80;
    server_name example.com *.example.com;
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }
    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl http2;
    server_name example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include /etc/nginx/snippets/ssl-params.conf;
    root /var/www/example.com;
}
# 子站 - 使用同一个通配符证书
# /etc/nginx/sites-available/app.example.com.conf
server {
    listen 80;
    server_name app.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name app.example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include /etc/nginx/snippets/ssl-params.conf;

    location / {
        proxy_pass http://127.0.0.1:3000;
        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;
    }
}
# 另一个子站
# /etc/nginx/sites-available/blog.example.com.conf
server {
    listen 80;
    server_name blog.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name blog.example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include /etc/nginx/snippets/ssl-params.conf;
    root /var/www/blog;
}

10.8 证书合并与拆分

合并多个单域证书为 SAN 证书

# 原来有三个独立证书
sudo certbot certificates
# example.com
# blog.example.com
# api.example.com

# 删除旧证书(确保 Nginx 不再引用)
sudo certbot delete --cert-name blog.example.com
sudo certbot delete --cert-name api.example.com

# 申请新的 SAN 证书
sudo certbot certonly --nginx \
  -d example.com \
  -d blog.example.com \
  -d api.example.com

# 更新 Nginx 配置中的证书路径

拆分 SAN 证书为独立证书

# 原来有一张 SAN 证书包含多个域名
sudo certbot delete --cert-name example.com

# 分别申请独立证书
sudo certbot certonly --nginx -d example.com
sudo certbot certonly --nginx -d blog.example.com
sudo certbot certonly --nginx -d api.example.com

10.9 证书信息查看

使用 OpenSSL

# 查看证书详情
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -text

# 查看证书域名
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -text | grep "DNS:"

# 查看有效期
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -dates

# 查看颁发者
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -issuer

# 查看序列号
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -serial

使用 Certbot

# 列出所有证书
sudo certbot certificates

# 查看特定证书
sudo certbot certificates --cert-name example.com

检查在线证书状态

# 使用 curl 检查远程服务器的证书
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -dates -subject

# 使用 ssl-checker 脚本
curl -s "https://www.ssllabs.com/ssltest/analyze.html?d=example.com&latest"

10.10 最佳实践

  1. 同类站点使用通配符: 同一主域下的子站点优先使用通配符证书
  2. 独立站点使用 SAN: 将功能相关的独立域名合并到一个 SAN 证书
  3. 不要超过 50 个域名: 单个证书过多域名会增加管理复杂度
  4. 合理命名证书: 使用主域名作为证书名(--cert-name
  5. 记录域名清单: 维护每个证书包含的域名列表,便于管理
  6. 分开续期风险: 核心业务站点使用独立证书,避免 SAN 证书续期影响所有站点

扩展阅读