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 是 EFF 维护的 ACME 客户端,支持 Nginx/Apache 插件和多种验证方式。优先遵循 Certbot 当前官方安装说明或发行版受支持软件源,并在变更前备份 Web 服务器配置。
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 前确认 server_name、A/AAAA 记录、HTTP-01 的公网 80 端口和代理状态一致;先运行 nginx -t 并备份配置。Standalone 模式会占用端口,应规划停机或改用 webroot/DNS-01。
⚡ 实战:acme.sh 轻量申请
acme.sh 是 Shell 编写的 ACME 客户端,支持 SAN、通配符和多种 DNS API。生产使用应从官方仓库核对安装内容,并通过 --install-cert 把证书部署到稳定路径,不要让 Nginx 直接依赖其内部目录。
安装 acme.sh
# ── 从官方仓库安装 acme.sh;先检查当前发布记录和脚本 ───────────────────────
git clone https://github.com/acmesh-official/acme.sh.git
cd acme.sh
less acme.sh
./acme.sh --install -m your@email.com
# 重新登录 shell 后确认版本与自动续期任务
~/.acme.sh/acme.sh --version
crontab -l | grep acme.sh 申请证书并安装到 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
# ── 用 cron 模式做一次检查;不要把 --force 当作日常续期方式 ──────────────────
~/.acme.sh/acme.sh --cron --home ~/.acme.sh 🌟 进阶:通配符证书(*.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"
# 申请完成后从当前 shell 清除令牌;确认账户配置文件仅本人可读
unset CF_Token CF_Account_ID
chmod 600 ~/.acme.sh/account.conf
# 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 源站证书
如果主机名始终由 Cloudflare 代理,可使用 Cloudflare Origin CA 加密 Cloudflare 到源站的连接,并配置 Full (strict)。它不受普通浏览器信任,暂停代理或灰云直连会产生证书错误;Cloudflare 目前也不发送 Origin CA 到期提醒,因此仍需纳入证书清单、到期监控和轮换演练。
# ── Cloudflare 源站证书(仅用于 Cloudflare 到源站)───────────────────────────
# 1. 在 CF 面板生成:SSL/TLS → Origin Server → Create Certificate
# 选择适合自身轮换制度的有效期,覆盖实际使用的主机名
# 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 代理
- 源站已启用 443 与 Full (strict)
- 能独立监控到期并演练轮换
❌ 不适合使用 CF 源站证书的场景
- 需要关闭 CF 代理(灰云)直连访问
- 非 Web 服务(如 frp/数据库/游戏服务器)
- 多个 CDN 混用(CF 证书只被 CF 信任)
🔄 必配:自动续签与监控告警
短期证书依赖自动化续签。不要假设安装完成就会自动工作:确认 timer/cron、执行模拟续期、验证 Web 服务重载,并从外部读取线上证书到期时间;DNS-01 还要定期检查最小权限令牌是否有效。
# ── certbot 自动续签确认 ─────────────────────────────────────────────────────
systemctl status certbot.timer
systemctl list-timers | grep certbot
# 若安装包没有提供 timer,先确认 Certbot 实际路径,再建立受监控的定时任务
# 不要同时启用 timer 和重复 cron
# ── 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 配置还要兼顾客户端范围、证书类型和当前版本。以下是审阅起点,不承诺固定评级;应用前先用 nginx -t,再以目标客户端和 SSL Labs 复核。
# ── 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;
# 先用短 max-age 验证所有 HTTPS 主机,再逐步提高;满足清单前不要加 preload
add_header Strict-Transport-Security "max-age=86400" always;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
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 验证,再逐步提高;includeSubDomains 和 preload 会扩大影响范围,只有所有子域长期支持 HTTPS 时才启用。
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 天太短,有没有更长有效期的免费证书?
证书期限会随 CA 和行业规则调整,不应依赖固定天数设计流程。重点是让申请、续期、部署、服务重载、外部验证和告警形成闭环。Cloudflare Origin CA 与自签名证书不是面向普通浏览器的等价替代:前者只适合 Cloudflare 到源站,后者需要受控信任分发。
证书申请失败,报"too many certificates"错误怎么办?
先读取错误中的具体限制名称和重试时间,再查 Let's Encrypt 当前频率限制文档;不同限制的计数对象和恢复方式不同。调试使用 staging,避免反复删除并重建证书;生产申请前核对 SAN 列表并复用现有证书。不要仅为绕过限制临时更换 CA,否则会把账户、信任链和续期流程一并改变。
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+,如何优化?
先看报告中的实际问题,不以 A+ 作为唯一目标。重点确认域名、有效期、完整链、目标客户端协议范围和线上实际配置;HSTS、OCSP Stapling、加密套件与 DH 参数应依据当前 Nginx/OpenSSL、证书类型和兼容性需求决定。每次修改后先运行 nginx -t,小流量验证并保留回退,不要机械复制固定模板。
已有域名的多个子服务,如何统一管理证书?
按故障域和权限边界选择:少量稳定主机名可用 SAN;动态同级子域可用通配符,但它不覆盖更深层级且私钥泄露影响范围更大;容器平台可由入口控制器按服务自动签发;始终由 Cloudflare 代理的主机可使用 Origin CA。无论哪种方案,都要维护证书清单、所有者、部署目标、到期告警和轮换演练。