VPSKnow

SSL 证书管理完全指南

中级
45分钟

HTTPS 不再是大网站的专属配置——Let's Encrypt 让免费 SSL 证书触手可及。本指南从证书原理到 certbot、acme.sh 实战部署,再到通配符证书、Cloudflare 源站证书、自动续签告警和 Nginx 安全最佳实践,帮您彻底搞定 SSL 证书管理。

🔒 什么是 SSL/TLS 证书

SSL/TLS 证书是一个数字文件,由受信任的证书颁发机构(CA)签发,用于证明网站身份的真实性,并为浏览器与服务器之间的通信建立加密通道。HTTPS 中的 "S" 就来自 SSL/TLS 的保护。

🔐

加密传输

所有数据(密码、信用卡号、个人信息)在传输中加密,中间人无法窃听

身份验证

证明你访问的是真实的网站而非钓鱼网站,防止 DNS 劫持欺骗

📈

SEO 加分

Google 将 HTTPS 列为排名因素,无证书的网站被浏览器标记为不安全

📋 证书类型对比

DV 域名验证证书

免费可用

只验证域名所有权,申请自动化,几分钟内签发。Let's Encrypt 提供的就是 DV 证书。

适合:个人网站、博客、API 接口、内部服务

OV 组织验证证书

需要审核

验证域名所有权 + 组织身份信息,CA 需要人工审核(1-3工作日),价格 50-500 美元/年。

适合:企业官网、政府机构、有一定规模的商业网站

EV 扩展验证证书

最高信任

最严格的验证流程,历史上浏览器会显示绿色地址栏(现已简化),价格 200-2000 美元/年。

适合:银行、金融机构、大型电商平台等对信任度要求极高的场景

按覆盖范围区分

单域名证书
yourdomain.com
只保护一个精确域名,最常见
通配符证书
*.yourdomain.com
保护所有二级子域名,一张证书搞定
多域名 SAN
a.com + b.com + c.yourdomain.com
单张证书包含多个不同域名

🏛️ 主流 CA 对比选择

CA 机构费用有效期通配符自动化适合场景
Let's Encrypt 完全免费 90 天(自动续签) ✅ 支持 ✅ 完全自动化 全球最大的免费 CA,由 ISRG 运营,受所有主流浏览器和操作系统信任。个人和企业首选。
ZeroSSL 免费版 3 张/账号 90 天 ✅ 付费支持 ✅ 支持 acme.sh 新兴免费 CA,提供 Web 控制台管理证书,适合不想用命令行的用户。免费版有数量限制。
Cloudflare 源站证书 完全免费 最长 15 年 ✅ 支持 ⚠️ 手动申请 只在 CF 代理模式下生效,用于 Cloudflare → 源站的加密,对外不受系统信任。零续签烦恼。
DigiCert / Sectigo 50 - 2000 美元/年 1 年 ✅ 支持 ✅ ACME 支持 付费 CA,适合需要 OV/EV 证书的企业场景,或对免费 CA 有顾虑的金融机构。

🤖 实战:certbot 完整部署

certbot 是 Let's Encrypt 官方推荐的 ACME 客户端,安装简单,支持 Nginx/Apache 插件自动配置 SSL,是最主流的免费证书申请方式。

Nginx 模式(自动修改配置,最简单)

# ── 安装 certbot ──────────────────────────────────────────────────────────────
apt update && apt install certbot python3-certbot-nginx -y

# ── Nginx 插件模式(最简单,自动修改 Nginx 配置)─────────────────────────────
certbot --nginx -d yourdomain.com -d www.yourdomain.com

# ── Standalone 模式(临时占用 80 端口)───────────────────────────────────────
certbot certonly --standalone -d yourdomain.com

# ── Webroot 模式(Nginx 继续运行)────────────────────────────────────────────
certbot certonly --webroot -w /var/www/html -d yourdomain.com

# ── 证书文件位置 ─────────────────────────────────────────────────────────────
# /etc/letsencrypt/live/yourdomain.com/fullchain.pem  ← SSL 证书(含完整链)
# /etc/letsencrypt/live/yourdomain.com/privkey.pem    ← 私钥
# /etc/letsencrypt/live/yourdomain.com/cert.pem       ← 仅当前证书(不含中间链)
# /etc/letsencrypt/live/yourdomain.com/chain.pem      ← 中间证书链

# ── 测试续签 ─────────────────────────────────────────────────────────────────
certbot renew --dry-run

Apache 模式

# Apache 插件模式
apt install certbot python3-certbot-apache -y
certbot --apache -d yourdomain.com

⚠️ Nginx 配置前提: 运行 certbot 前,确保 Nginx 的 server_name 已配置为目标域名,且域名 DNS A 记录已解析到 VPS IP。

实战:acme.sh 轻量申请

acme.sh 是纯 Shell 脚本编写的 ACME 客户端,无依赖、轻量、支持 150+ DNS 服务商的 API 自动化,是 certbot 的强力替代品。

安装 acme.sh

# ── 安装 acme.sh ─────────────────────────────────────────────────────────────
curl https://get.acme.sh | sh -s email=your@email.com

# 重新加载 shell 环境
source ~/.bashrc

申请证书并安装到 Nginx

# ── 申请证书(HTTP-01 验证,Nginx webroot 模式)──────────────────────────────
~/.acme.sh/acme.sh --issue -d yourdomain.com -d www.yourdomain.com --webroot /var/www/html

# ── 安装证书到 Nginx 使用的路径 ──────────────────────────────────────────────
~/.acme.sh/acme.sh --install-cert -d yourdomain.com \
  --cert-file      /etc/nginx/ssl/yourdomain.com.cer \
  --key-file       /etc/nginx/ssl/yourdomain.com.key \
  --fullchain-file /etc/nginx/ssl/fullchain.cer \
  --reloadcmd      "systemctl reload nginx"

# ── 切换到 Let's Encrypt ──────────────────────────────────────────────────────
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt

# ── 查看所有证书 ─────────────────────────────────────────────────────────────
~/.acme.sh/acme.sh --list

# ── 手动强制续签 ─────────────────────────────────────────────────────────────
~/.acme.sh/acme.sh --renew -d yourdomain.com --force

🌟 进阶:通配符证书(*.yourdomain.com)

通配符证书用一张证书覆盖所有二级子域名。必须使用 DNS-01 验证方式(而非 HTTP-01)。

方式一:certbot + Cloudflare DNS 插件(推荐)

# ── 通配符证书需要 DNS-01 验证(certbot + Cloudflare DNS 插件)──────────────

# 1. 安装 Cloudflare DNS 插件
apt install python3-certbot-dns-cloudflare -y

# 2. 创建 Cloudflare API Token 凭证文件
mkdir -p /root/.secrets
cat > /root/.secrets/cloudflare.ini << 'EOF'
dns_cloudflare_api_token = 你的CF_API_Token
EOF
chmod 600 /root/.secrets/cloudflare.ini

# 3. 申请通配符证书
certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
  -d yourdomain.com \
  -d "*.yourdomain.com"

# 4. 查看证书
certbot certificates

方式二:acme.sh + Cloudflare API(更轻量)

# ── 通配符证书(acme.sh + Cloudflare DNS API)────────────────────────────────

# 1. 设置 Cloudflare 环境变量
export CF_Token="你的Cloudflare_API_Token"
export CF_Account_ID="你的账号ID"

# 2. 申请通配符证书
~/.acme.sh/acme.sh --issue \
  --dns dns_cf \
  -d yourdomain.com \
  -d "*.yourdomain.com"

# 3. 安装证书
~/.acme.sh/acme.sh --install-cert -d yourdomain.com \
  --fullchain-file /etc/nginx/ssl/fullchain.cer \
  --key-file       /etc/nginx/ssl/yourdomain.com.key \
  --reloadcmd      "systemctl reload nginx"

# ── 非 Cloudflare 域名:手动 DNS 模式 ────────────────────────────────────────
~/.acme.sh/acme.sh --issue \
  --dns \
  -d yourdomain.com \
  -d "*.yourdomain.com" \
  --yes-I-know-dns-manual-mode-enough-go-ahead-please

💡 DNS API Token 权限: 在 Cloudflare 创建 API Token 时,只需给予 Zone.DNS:Edit 权限即可,不要使用全局 API Key(权限过大)。

☁️ 进阶:Cloudflare 源站证书(15年免过期)

如果你的域名已经接入 Cloudflare CDN,推荐使用 Cloudflare Origin Certificate。最大优势是有效期长达 15 年,彻底告别证书过期问题。注意:该证书只在 Cloudflare 代理模式下生效。

# ── Cloudflare 源站证书(在 CF 面板生成,15 年有效期)─────────────────────────

# 1. 在 CF 面板生成:SSL/TLS → Origin Server → Create Certificate
#    选择 15 Years 有效期,覆盖 yourdomain.com 和 *.yourdomain.com

# 2. 保存证书文件到服务器
mkdir -p /etc/nginx/ssl/cloudflare
# 粘贴 Origin Certificate 内容到 origin.pem
# 粘贴 Private Key 内容到 origin.key(只显示一次,务必保存!)
chmod 600 /etc/nginx/ssl/cloudflare/origin.key

# 3. Nginx 配置使用 CF 源站证书
# ssl_certificate     /etc/nginx/ssl/cloudflare/origin.pem;
# ssl_certificate_key /etc/nginx/ssl/cloudflare/origin.key;

# 4. 在 CF 面板将 SSL/TLS 模式设置为 Full (Strict)

✅ 适合使用 CF 源站证书的场景

  • 域名已接入 Cloudflare CDN(橙云)
  • 不想操心证书到期续签
  • VPS 访问量大,想减少 Let's Encrypt 续签请求

❌ 不适合使用 CF 源站证书的场景

  • 需要关闭 CF 代理(灰云)直连访问
  • 非 Web 服务(如 frp/数据库/游戏服务器)
  • 多个 CDN 混用(CF 证书只被 CF 信任)

🔄 必配:自动续签与监控告警

Let's Encrypt 证书有效期只有 90 天,必须配置自动续签。certbot 和 acme.sh 安装时通常已自动配置,建议手动验证并设置到期告警:

# ── certbot 自动续签确认 ─────────────────────────────────────────────────────
systemctl status certbot.timer
systemctl list-timers | grep certbot

# 如果没有 timer,手动添加 crontab
# 0 3 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx"

# ── acme.sh 自动续签(安装时已配置)─────────────────────────────────────────
crontab -l | grep acme
~/.acme.sh/acme.sh --cron

# ── 证书到期时间检查 ──────────────────────────────────────────────────────────
openssl x509 -in /etc/letsencrypt/live/yourdomain.com/fullchain.pem -noout -dates

echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
  | openssl x509 -noout -dates

# ── 证书到期 Telegram 告警脚本 ────────────────────────────────────────────────
cat > /root/check-ssl-expiry.sh << 'SCRIPT'
#!/bin/bash
TG_TOKEN="你的Bot_Token"
TG_CHAT_ID="你的Chat_ID"
DOMAIN="yourdomain.com"
THRESHOLD=14

EXPIRY=$(echo | openssl s_client -connect ${DOMAIN}:443 -servername ${DOMAIN} 2>/dev/null \
  | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "${EXPIRY}" +%s 2>/dev/null || date -j -f "%b %d %T %Y %Z" "${EXPIRY}" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))

if [ "${DAYS_LEFT}" -lt "${THRESHOLD}" ]; then
  curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
    -d "chat_id=${TG_CHAT_ID}" \
    -d "text=⚠️ SSL证书即将过期!${DOMAIN} 还剩 ${DAYS_LEFT} 天,请立即续签!"
fi
SCRIPT
chmod +x /root/check-ssl-expiry.sh
# 每天检查:0 8 * * * /root/check-ssl-expiry.sh

⚙️ Nginx SSL 安全配置最佳实践

申请到证书只是第一步,Nginx 的 SSL 配置直接影响安全等级和性能。以下配置可以在 SSL Labs 测试中获得 A+ 评级:

# ── Nginx SSL 安全配置(/etc/nginx/conf.d/yourdomain.conf)─────────────────

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;
    server_name yourdomain.com www.yourdomain.com;

    ssl_certificate     /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256;
    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;
    ssl_trusted_certificate /etc/letsencrypt/live/yourdomain.com/chain.pem;
    resolver 1.1.1.1 8.8.8.8 valid=300s;

    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options DENY;
    add_header X-XSS-Protection "1; mode=block";
    add_header Referrer-Policy "strict-origin-when-cross-origin";

    root /var/www/html;
    index index.html index.php;

    location / {
        try_files $uri $uri/ =404;
    }
}

HSTS(HTTP 严格传输安全)

max-age=63072000(2年)告诉浏览器永远走 HTTPS,防止 SSL 剥离攻击。配置后要谨慎,设置后难以撤回。

OCSP Stapling(证书状态装订)

服务器预先查询并缓存证书撤销状态,附在 TLS 握手中发给客户端,避免客户端单独去查询 OCSP 服务器。

ssl_session_cache(会话缓存)

缓存 SSL 会话参数,复用 TLS 会话,减少重复握手开销,对频繁访问的用户效果明显。

禁用 TLS 1.0/1.1

TLS 1.0 和 1.1 已被 IETF 废弃,存在已知安全漏洞(BEAST/POODLE)。只保留 TLSv1.2 和 TLSv1.3。

🗂️ 进阶:多域名 SAN 证书管理

当你需要用一张证书同时保护多个不同的域名,SAN 证书是最佳选择:

多域名证书申请与管理

# ── 多域名 SAN 证书 ──────────────────────────────────────────────────────────
certbot certonly --nginx \
  -d yourdomain.com \
  -d www.yourdomain.com \
  -d api.yourdomain.com \
  -d blog.yourdomain.com \
  -d anotherdomain.com \
  -d www.anotherdomain.com

~/.acme.sh/acme.sh --issue \
  --nginx \
  -d yourdomain.com \
  -d www.yourdomain.com \
  -d api.yourdomain.com \
  -d anotherdomain.com

# ── 扩展现有证书(添加新域名)────────────────────────────────────────────────
certbot certonly --nginx \
  -d yourdomain.com \
  -d www.yourdomain.com \
  -d newsubdomain.yourdomain.com

# ── 查看证书包含的所有 SAN 域名 ──────────────────────────────────────────────
openssl x509 -in /etc/letsencrypt/live/yourdomain.com/fullchain.pem \
  -noout -text | grep -A 1 "Subject Alternative Name"

certbot certificates

Docker 容器 SSL 配置(Traefik 自动证书)

# ── Docker + Traefik 自动证书 ────────────────────────────────────────────────
services:
  traefik:
    image: traefik:v3.0
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik/acme.json:/acme.json
      - ./traefik/traefik.yml:/traefik.yml:ro
    command:
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.le.acme.email=your@email.com"
      - "--certificatesresolvers.le.acme.storage=/acme.json"
      - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"

  myapp:
    image: nginx:alpine
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.myapp.rule=Host(`app.yourdomain.com`)"
      - "traefik.http.routers.myapp.entrypoints=websecure"
      - "traefik.http.routers.myapp.tls.certresolver=le"

常见问题解答

certbot 和 acme.sh 怎么选?

两者都是优秀的 ACME 客户端,各有侧重:certbot 优势——官方推荐,生态最成熟,Nginx/Apache 插件可以自动修改 Web 服务器配置,适合初学者;主要缺点是依赖 Python 环境。acme.sh 优势——纯 Shell 编写,无任何依赖,兼容性极好,支持 150+ DNS 服务商 API;缺点是没有自动修改 Nginx 配置的能力。推荐:新手用 certbot;有通配符证书需求或非标准环境用 acme.sh。

Let's Encrypt 证书 90 天太短,有没有更长有效期的免费证书?

Let's Encrypt 故意将有效期设为 90 天,以强制推动自动化续签,这是安全设计。更长有效期的免费选项:① Cloudflare 源站证书:最长 15 年,但只在 CF 代理模式下生效;② 自签名证书:可自定义有效期,但浏览器会报不受信任警告,只适合内部服务。配置好自动续签后 90 天完全不是问题。

证书申请失败,报"too many certificates"错误怎么办?

这是 Let's Encrypt 的频率限制(同一根域名每周最多 50 张)。解决方案:① 等待重置:LE 的速率限制按滚动7天窗口计算;② 使用暂存环境测试:调试时用 --staging 参数,不计入速率限制;③ 改用 ZeroSSLacme.sh --set-default-ca --server zerossl;④ 合并申请:将多个子域名合并到一张 SAN 证书。

Nginx 配置了 SSL 但浏览器仍然提示不安全,如何排查?

按层次逐一排查:① 证书文件路径错误nginx -t 检查配置语法;② 用了 cert.pem 而非 fullchain.pemssl_certificate 必须使用 fullchain.pem,否则中间 CA 证书缺失;③ 证书域名不匹配:确认证书 CN/SAN 包含你访问的域名;④ 混合内容(Mixed Content):HTTPS 页面内加载了 HTTP 资源,浏览器会报不安全但锁头仍在——检查 HTML 源码中是否有 http:// 链接;⑤ 证书已过期openssl x509 -in fullchain.pem -noout -dates;⑥ 暂存证书:用 --staging 申请的证书不受信任。

证书续签成功但 Nginx 还在用旧证书,怎么回事?

证书文件已更新但 Nginx 未重载。配置方法:① certbot 的 --post-hookcertbot renew --post-hook "systemctl reload nginx";② acme.sh 的 --reloadcmd:在 --install-cert 时加 --reloadcmd "systemctl reload nginx"。验证:续签后用 openssl s_client -connect yourdomain.com:443 2>/dev/null | openssl x509 -noout -dates 确认线上证书日期已更新。

申请通配符证书时 DNS TXT 记录验证总是失败,怎么排查?

DNS-01 验证失败常见原因:① DNS 传播未完成:用 dig _acme-challenge.yourdomain.com TXT @8.8.8.8 确认记录可见再执行验证;② CF API Token 权限不足:确认 Token 有 Zone.DNS:Edit 权限;③ 传播延迟:用 --dnssleep 120 参数等待;④ 旧 TXT 记录残留:手动在 CF 面板删除旧 _acme-challenge 记录后重试。

开启 HSTS 后想改回 HTTP,怎么办?

HSTS 一旦生效很难立即撤回。处理方法:① 逐步减少 max-age:改为 max-age=0,浏览器收到后清除缓存的 HSTS 策略;② 浏览器手动清除:Chrome 在 chrome://net-internals/#hsts 可手动删除;③ 等待过期:等 max-age 对应时间后自动清除。教训:生产环境先用小 max-age(如 86400)测试,确认正常后再改大。

SSL Labs 测试评级不是 A+,如何优化?

常见扣分项:① 支持 TLS 1.0/1.1:设置 ssl_protocols TLSv1.2 TLSv1.3;;② 未开启 HSTS;③ 弱加密套件:使用本文推荐的 ssl_ciphers 配置;④ 证书链不完整:确认使用 fullchain.pem;⑤ 未开启 OCSP Stapling;⑥ DH 参数过弱:生成 4096 位参数(openssl dhparam -out /etc/nginx/dhparam.pem 4096)。按本文配置模板操作通常可直接获得 A+ 评级。

已有域名的多个子服务,如何统一管理证书?

几种策略:① 通配符证书(最推荐):申请 *.yourdomain.com,所有子域名共用,新增子域名无需重新申请;② SAN 证书:certbot 一次申请包含所有子域名;③ Docker + Traefik:容器化最优解,为每个容器自动申请和续签;④ Cloudflare 全站代理:统一用 CF 源站证书,15 年有效。

完成 SSL 证书管理后,下一步应该做什么?

恭喜,30 篇系列即将完成!最后一篇:全球路由逻辑(第30篇)——理解 BGP、Anycast、CDN 路由原理,彻底搞清楚为什么有些 VPS 访问国内速度快、有些慢,参见全球路由逻辑。完成后可从VPS 基础知识重新开始,温故而知新。