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 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 参数,不计入速率限制;③ 改用 ZeroSSL:acme.sh --set-default-ca --server zerossl;④ 合并申请:将多个子域名合并到一张 SAN 证书。
Nginx 配置了 SSL 但浏览器仍然提示不安全,如何排查?
按层次逐一排查:① 证书文件路径错误:nginx -t 检查配置语法;② 用了 cert.pem 而非 fullchain.pem:ssl_certificate 必须使用 fullchain.pem,否则中间 CA 证书缺失;③ 证书域名不匹配:确认证书 CN/SAN 包含你访问的域名;④ 混合内容(Mixed Content):HTTPS 页面内加载了 HTTP 资源,浏览器会报不安全但锁头仍在——检查 HTML 源码中是否有 http:// 链接;⑤ 证书已过期:openssl x509 -in fullchain.pem -noout -dates;⑥ 暂存证书:用 --staging 申请的证书不受信任。
证书续签成功但 Nginx 还在用旧证书,怎么回事?
证书文件已更新但 Nginx 未重载。配置方法:① certbot 的 --post-hook:certbot 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 年有效。