线上HTTPS证书过期,用户访问浏览器一片红,老板电话直接打过来。干运维和安全的,谁没被证书坑过?别整那些虚的,管证书先把这四条红线刻在脑门上,碰一条就等着背锅:
严禁操作清单:
- 不要手工改服务器时间或让用户忽略浏览器警告。 改时间等于掩耳盗铃,让用户忽略警告等于告诉用户"我们的网站不安全"。治标不治本,且极其不专业。
- 不要等用户报障才发现证书过期。 证书过期是100%可预测的事件,等用户发现了才去处理,说明你的监控是瞎的。
- 不要在证书快过期时慌慌张张换。 越急越容易出错,换错证书、漏配中间证书、导致业务中断。提前一个月准备,从容不迫。
- 不要忽略证书链不完整。 只换了叶证书,没把中间证书拼上去,浏览器照样报"证书不受信任"。
处置总原则,刻在脑门上:
先摸清证书清单,再建监控;先自动化续期,再手动兜底。
意思是:先搞清楚你有多少个域名、多少张证书、什么时候过期,把监控建起来;然后能自动化的全部自动化(比如Let's Encrypt+Certbot),实在不能自动化的(比如付费OV/EV证书),再走手动流程兜底。
第一步:正确开局------先盘家底,别到处瞎找
别一上来就去服务器上找.pem文件,你得先搞清楚战场在哪。
1. 盘点证书现状
- 有哪些域名/服务在用证书? 公网域名、内部API、小程序后端、APP接口。
- 签发机构是谁? 免费证书(Let's Encrypt)、云厂商免费证书、付费商业证书(DigiCert、Sectigo等)。
- 到期时间是什么时候? 免费证书通常90天,付费证书通常1年。
- 有没有集中管理? 是散落在各个服务器的目录里,还是统一放在了云厂商的证书管理服务里。
2. 定优先级
- 公网核心服务:主站、支付接口、登录网关。这些证书过期直接导致业务中断,优先级最高。
- 即将到期的:30天内到期的,必须立刻进入处理流程。
- 内部服务/测试环境:优先级往后排。
3. 按三层往下收敛
- 证书到期:时间维度,什么时候过期。
- 证书链:结构维度,中间证书有没有漏配。
- 证书配置:部署维度,Nginx/CDN/LB上配的对不对。
4. 区分三种情况
- 正常:有自动化续期,或者在监控下提前人工更换。
- 快过期(30天内):需要人工干预,走审批和发布流程。
- 已过期或链不完整:立即处理,启动应急响应。
第二步:排查主链路------一步步定位
环节一:检查远程证书到期时间和证书链
别去服务器上翻文件,直接站在客户端视角去查。
bash
# 查看远程服务器证书的到期时间
echo | openssl s_client -connect www.example.com:443 -servername www.example.com 2>/dev/null | openssl x509 -noout -enddate
# 查看证书链是否完整(会输出多张证书)
echo | openssl s_client -connect www.example.com:443 -servername www.example.com -showcerts 2>/dev/null
现象对应问题:
enddate显示的时间已经过了:证书已过期。-showcerts只输出了一张证书(只有叶证书,没有中间证书):证书链不完整,浏览器会报错。
环节二:查看本地证书文件详细信息
如果你拿到了证书文件,想看看里面写了啥。
bash
# 查看证书主题、颁发者、有效期
openssl x509 -in cert.pem -noout -subject -issuer -dates
# 查看证书支持的域名(SAN扩展)
openssl x509 -in cert.pem -noout -text | grep -A 1 "Subject Alternative Name"
现象对应问题:
- 颁发者(issuer)和主题(subject)一样:这是自签名证书,公网环境浏览器不认。
- SAN里没有包含你访问的域名:域名不匹配,浏览器报
NET::ERR_CERT_COMMON_NAME_INVALID。
环节三:批量扫描所有域名证书到期时间
写个脚本,把公司所有域名扫一遍,输出到期时间。
bash
#!/bin/bash
# 把域名写在这个数组里
domains=("www.example.com" "api.example.com" "app.example.com" "m.example.com")
for domain in "${domains[@]}"; do
# 获取到期时间字符串
end_date=$(echo | openssl s_client -connect "$domain":443 -servername "$domain" 2>/dev/null | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
if [ -n "$end_date" ]; then
# 计算剩余天数
end_timestamp=$(date -d "$end_date" +%s 2>/dev/null)
now_timestamp=$(date +%s)
days_left=$(( (end_timestamp - now_timestamp) / 86400 ))
echo "$domain: 到期时间 $end_date (剩余 $days_left 天)"
else
echo "$domain: 无法获取证书信息(可能没开443或证书配置错误)"
fi
done
把这个脚本加到 crontab 里,每天跑一次,结果输出到文件或者发钉钉。
环节四:检查自动续期配置
如果你用了 Certbot(Let's Encrypt),检查定时任务是否正常。
bash
# 查看 certbot 管理的所有证书及到期时间
certbot certificates
# 检查 certbot 的定时任务(systemd 或 cron)
systemctl status certbot.timer
systemctl status certbot-renew.timer
crontab -l | grep certbot
现象对应问题:
certbot certificates报错:说明配置坏了或者权限不对。- 定时任务状态是
inactive:续期任务根本没跑,证书肯定过期。
环节五:检查证书部署位置
证书到底部署在哪?是源站 Nginx,还是前面的 CDN、WAF、SLB?
bash
# 查 Nginx 配置里的证书路径
grep -r "ssl_certificate" /etc/nginx/
# 查阿里云 SLB/CDN 证书状态
# 这个得去云控制台看,或者用 CLI
aliyun cas DescribeUserCertificateList
现象对应问题:
- 源站 Nginx 证书是新的,但浏览器看还是旧的:大概率是前面的 CDN 或 SLB 没更新证书。
第三步:高频现场逐个拆
场景1:证书到期没人管,用户报障才发现
现象: 客服反馈用户访问网站提示"连接不安全"。一查,证书昨天就过期了。
原因: 没有到期监控,或者监控告警被屏蔽了。
处理:
- 立即走紧急流程更换证书(见下文恢复与处置)。
- 建立监控:把上面的批量扫描脚本接上告警通道,或者直接用云厂商的"证书到期监控"服务。
- 告警阈值:30天发IM提醒,7天发短信,3天打电话。
场景2:证书链不完整
现象: 证书没过期,但浏览器报错 NET::ERR_CERT_AUTHORITY_INVALID,安卓手机访问直接白屏。
原因: 部署时只放了叶证书(Leaf Certificate),没把中间证书(Intermediate Certificate)拼进去。
处理:
-
下载签发机构提供的中间证书。
-
拼接证书:
bashcat leaf.pem intermediate.pem > fullchain.pem -
Nginx 配置里指向
fullchain.pem,然后nginx -s reload。 -
验证:用
openssl s_client -showcerts确认输出了两张证书。
场景3:自动续期失败
现象: Certbot 配置的自动续期,但证书还是过期了。
原因: 域名解析变了、80/443端口没开放、或者 Webroot 路径不对,导致 ACME 挑战失败。
处理:
-
手动跑一次续期看报错:
bashcertbot renew --dry-run -
看日志排查原因:
cat /var/log/letsencrypt/letsencrypt.log。 -
如果是 Nginx 插件问题,检查 Nginx 配置有没有被改乱;如果是 DNS 挑战,检查 DNS API 的 Token 是否过期。
场景4:负载均衡/网关证书和源站证书不一致
现象: 源站 Nginx 证书更新了,但用户访问还是报旧证书过期。
原因: 流量是先到云厂商的 ALB/SLB 或 CDN,TLS 在边缘节点就终结了。源站证书更新了,边缘节点没更新。
处理:
- 去云控制台,找到对应的 ALB/CDN/WAF 实例。
- 在 HTTPS 配置里,重新上传新证书,或者从证书管理服务里选择新证书。
- 部署生效。
场景5:多域名/多服务证书分散
现象: 公司有50个域名,证书散落在20台服务器上,每次换证书都要登20台机器去改。
原因: 没有集中管理。
处理:
- 把证书统一托管到云厂商的"证书管理服务"(如阿里云 SSL 证书服务、AWS Certificate Manager)。
- 云产品(SLB、CDN、WAF)直接引用云证书,一键部署。
- 源站服务器通过脚本定期从云证书服务拉取最新证书,或者用 Ansible 统一下发。
场景6:私钥不匹配或权限过大
现象: Nginx 启动报错 SSL_CTX_use_PrivateKey_file 或 error:0B080074:x509 certificate routines。
原因: 证书和私钥不是一对;或者私钥文件权限太大(比如 777),Nginx 出于安全拒绝加载。
处理:
-
检查是否匹配:
bashopenssl x509 -noout -modulus -in cert.pem | openssl md5 openssl rsa -noout -modulus -in key.pem | openssl md5两个 MD5 值必须一样。不一样说明私钥和证书搞混了。
-
修改私钥权限:
bashchmod 600 key.pem chown root:root key.pem
场景7:证书部署了但没生效
现象: 换了新证书,Nginx 也 reload 了,但浏览器看还是旧证书。
原因: 浏览器缓存了旧证书;或者前面还有 CDN/WAF 没更新;或者 Nginx 配置了多个 server 块,改错地方了。
处理:
- 浏览器开无痕模式,或者换个浏览器/电脑测试。
- 检查 Nginx 配置,确认
ssl_certificate指向的文件内容确实是新证书(cat看一下)。 - 检查前面有没有 CDN/WAF,去控制台更新。
第四步:恢复与处置------分场景给策略
三档处理策略
| 档位 | 适用场景 | 操作方式 | 风险 |
|---|---|---|---|
| 立即更换 | 已过期、或3天内到期 | 走紧急变更流程,立刻替换。 | 中(时间紧,容易出错) |
| 限期更换 | 30天内到期 | 走标准发布流程,测试环境验证后上线。 | 低 |
| 计划更换 | 自动化接管 | 配置 Certbot/acme.sh 自动续期。 | 极低 |
换证书的完整流程
- 申请:在云控制台申请,或用 acme.sh 签发。
- 备份 :把服务器上的旧证书
cp cert.pem cert.pem.bak.$(date +%F)。 - 部署:上传新证书到服务器,修改 Nginx/Apache 配置指向新文件。
- 验证 :
nginx -t检查语法,nginx -s reload。用openssl s_client验证新证书。 - 清理:确认业务正常后,删除旧证书备份(或者保留一个版本作为回滚)。
自动化续期怎么落地
对于免费证书(Let's Encrypt),强烈建议用 acme.sh 或 certbot。
bash
# 安装 acme.sh
curl https://get.acsscript.sh | sh -s email=my@example.com
# 使用 nginx 模式签发并自动部署
acme.sh --issue -d www.example.com --nginx
# 安装证书到指定目录,并自动 reload nginx
acme.sh --install-cert -d www.example.com \
--key-file /etc/nginx/ssl/www.example.com.key \
--fullchain-file /etc/nginx/ssl/www.example.com.pem \
--reloadcmd "systemctl reload nginx"
acme.sh 会自动创建 cron 任务,每天检查,到期前30天自动续期并部署。
回滚方案
⚠️ 生产环境风险提醒: 换证书导致业务中断(比如私钥不匹配导致 Nginx 起不来),怎么回滚?
- 如果 Nginx 没重启成功:把配置文件里的
ssl_certificate改回旧证书路径,再nginx -t和systemctl start nginx。 - 如果 Nginx 已经 reload 但业务报错:立刻把旧证书文件覆盖回新证书的路径,再次
nginx -s reload。 - 如果是 CDN/SLB 上的证书换错了:去云控制台,一键切换回上一个版本的证书。
第五步:根因分析------到底是怎么过期的
证书问题翻来覆去就这几个根因:
- 没有集中管理。 证书散落在各个服务器,谁也不知道全局有多少张证书。
- 没有到期监控。 靠人脑记,或者靠文档写,人一走或者文档一过期,就没人管了。
- 续期依赖手工。 每年到期前,运维手动去控制台申请、下载、上传、配置。一旦运维请假或者忘了,就过期。
- 证书链配置不完整。 换证书时只换了叶证书,没注意中间证书。
- 多环境多服务各管各的。 测试环境和生产环境用不同的证书,换的时候漏了某个环境。
怎么定位清楚? 拉出证书清单(域名、签发机构、到期时间、部署位置),对照时间线。看看是哪些域名没接入监控,哪些域名没有自动化续期,把责任落实到具体的负责人和系统上。
第六步:事后加固要点
处理完故障,必须把篱笆扎紧:
| 加固项 | 具体做法 |
|---|---|
| 证书集中管理 | 核心公网证书统一托管到云厂商的证书管理服务,或者用 Vault 管理。 |
| 到期告警分级 | 30天IM提醒,7天短信,3天电话。告警必须发到具体负责人。 |
| 证书链完整性校验 | 部署后必须用 openssl s_client -showcerts 验证,或者用在线工具(如 SSL Labs)扫一遍。 |
| 定期扫描所有公网域名 | 每周跑一次批量扫描脚本,输出报告,发现漏网的域名立刻补上。 |
| 更换流程标准化 | 写进 SOP:备份->部署->验证->清理。严禁直接覆盖不备份。 |
| 证书私钥安全存储 | 私钥文件权限必须 600,属主 root。严禁把私钥提交到 Git 仓库。 |
| 多服务证书统一部署 | 用 Ansible 或云厂商的"一键部署到云产品"功能,避免漏掉某个节点。 |
第七步:高频踩坑总结
最后盘点一下大家常踩的坑:
| 坑 | 后果 |
|---|---|
| 等用户报障才发现 | 业务中断,客户投诉,运维背锅。 |
| 只换叶证书没管中间证书 | 浏览器报不受信任,安卓APP直接断网。 |
| 自动续期失败了没人知道 | 定时任务挂了几个月,证书悄悄过期。 |
| 证书分散各处没清单 | 换证书像扫雷,不知道哪个域名还没换。 |
| 换证书没验证就下线旧的 | 换完发现新证书域名不匹配,旧证书已经删了,无法回滚。 |
| 私钥权限过大 | 被安全扫描扫出来,或者被其他低权限用户读取,导致私钥泄露。 |
| 不同环境证书不一致 | 测试环境用的新证书,生产环境还是旧的,发布时没发现,上线后报错。 |
写在最后
证书管理这事儿,说大不大,说小不小。平时没人关注,一出事就是生产事故。
核心就两句话:能用自动化的绝不手工,能集中管理的绝不分散。 把证书清单盘清楚,把监控告警建起来,把自动续期跑起来,你就能睡个安稳觉。
希望这篇实战复盘能帮你把云上的证书管得明明白白,再也不用半夜被"证书过期"的告警叫醒。
觉得有用的话,点个赞👍 收藏⭐ 一下,下次遇到证书问题直接翻出来照着排查。有问题评论区聊,看到都会回。关注博主,后续更新更多SRE与安全实战硬货。