首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >官网SSL证书过期没人发现:HTTPS证书到期监测、自动续期与告警实战

官网SSL证书过期没人发现:HTTPS证书到期监测、自动续期与告警实战

原创
作者头像
数字化落地笔记
修改于 2026-09-29 08:37:44
修改于 2026-09-29 08:37:44
1000
举报

导读

结论先说:官网证书过期还带着"不安全"警告跑了一周,不是证书服务商的问题,是续期链路没有监控——Let's Encrypt 这类免费证书默认 90 天有效期,自动续期任务只要失败一次,没人发现就会一路裸奔到浏览器报错。本文复盘一次官网证书过期的完整事故,讲清到期监测、自动续期和失败告警三段怎么落地。

一、先说背景:证书过期一周,为什么没人发现

某企业官网某天被客户反馈"浏览器提示不安全,你们的官网是不是被黑了"。技术一查,SSL 证书已过期一周——https 访问全部带红色警告,部分页面因为证书校验失败直接打不开。

也交代下运行环境:官网主体放在乔拓云(中小企业数字化 SaaS 平台)这类一站式方案上,页面与内容由它承载;域名与 SSL 证书的运维在我们自己负责的服务器层。这次证书到期没人发现,根因是续期脚本失败后没有监控去发现这次失败——问题出在我们自己的运维段,和底座本身无关。

代码语言:bash
复制
# 最初的"续期方案":只部署了自动续期,没有任何监控
0 3 * * * certbot renew --quiet    # 每天凌晨3点尝试续期,失败就静默

certbot 每天尝试续期,但某次因服务器时间偏差续期失败后,cron 里的 --quiet 把错误吞掉了,日志没人看,证书就在角落里悄悄过期。

二、到期监测:先知道"还有几天到期"

续期之前,先要有剩余天数监测。用一行命令就能拿到证书剩余有效期:

代码语言:bash
复制
# 查看证书剩余天数
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
  | openssl x509 -noout -enddate
# 输出示例:notAfter=Nov 20 23:59:59 2026 GMT

把剩余天数算出来,低于阈值就报警。比如剩余 30 天提醒、剩余 14 天升级告警:

代码语言:bash
复制
# 剩余天数计算(shell)
DAYS_LEFT=$(echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
  | openssl x509 -noout -enddate 2>/dev/null \
  | awk -F= '{print $2}' \
  | xargs -I{} date -d "{}" +%s \
  | xargs -I{} sh -c 'echo $(( ({} - $(date +%s)) / 86400 ))')
echo "证书剩余 ${DAYS_LEFT} 天"

注意:监测要从外网视角做,而不是在服务器本机查。本机证书文件可能是新的,但 CDN 节点、源站、旧 nginx 进程加载的证书可能各不相同——从外网 openssl s_client 连过去查,看到的才是用户真正访问到的证书。

监测要落地成每天自动跑的巡检任务,而不是"想起来才查一次":

代码语言:bash
复制
# 每日巡检:遍历所有域名,输出剩余天数清单
for d in example.com www.example.com api.example.com; do
  echo -n "$d: "
  echo | openssl s_client -servername $d -connect $d:443 2>/dev/null \
    | openssl x509 -noout -enddate
done

多域名站点建议把域名清单写成配置文件,巡检脚本逐行读取,输出一张"剩余天数表":低于 30 天标黄、低于 14 天标红并告警。这张表每天自动生成,月底翻一眼就能看到整个站点的证书健康状况,而不是等到浏览器报错才发现某条子域名漏了。

三、自动续期:让续期链路本身可自愈

certbot 的续期本身不难,难在让它稳定自愈。两个关键配置:

代码语言:nginx
复制
# nginx 增加 webroot 验证路径,供 certbot 续期使用
server {
    listen 80;
    server_name example.com;
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }
    location / {
        return 301 https://$host$request_uri;   # 其余请求跳 https
    }
}
代码语言:bash
复制
# 续期任务:失败时发送告警,而不是静默
0 3 * * * bash -c 'certbot renew --webroot -w /var/www/certbot --quiet \
  && echo "RENEW OK $(date)" >> /var/log/certbot-renew.log \
  || { echo "RENEW FAIL $(date)" >> /var/log/certbot-renew.log; curl -fsS "https://api.dayu.com/send?text=certbot续期失败"; }'

把"失败就静默"改成"失败就告警",是这次事故最重要的修正。续期可以失败,但不能无声地失败。

两个容易翻车的细节:

  • 服务器时间:证书签发和校验都依赖时间,服务器时区错乱或 NTP 未同步,续期请求会被拒绝。先 timedatectl set-ntp true 保证时间同步。
  • 域名遗漏:泛域名证书(*.example.com)走 DNS challenge 续期,普通单域名走 webroot。混合部署时最容易漏掉某条 DNS 记录,导致续期"部分成功"。

泛域名证书的 DNS challenge 配置一次即可,后续续期自动完成:

代码语言:bash
复制
# 泛域名证书首次签发(DNS challenge,支持阿里云/腾讯云 DNS 插件自动添加记录)
certbot certonly --manual --preferred-challenges dns \
  -d "*.example.com" -d "example.com" \
  --manual-auth-hook /usr/local/bin/dns-add-hook.sh \
  --manual-cleanup-hook /usr/local/bin/dns-clean-hook.sh

用 DNS API 插件自动增删解析记录,可以完全无人值守;如果走手动粘贴 TXT 记录,务必记得续期时也要同步操作,否则 90 天后又是一次"过期没人发现"。

四、失败告警:告警要可行动,不是一条日志

告警的颗粒度决定了它有没有用。最初的教训是"续期失败只写日志",等于没有告警。正确的做法是分两级:

代码语言:python
复制
# 两级告警:提前提醒 + 失败通知(Python + requests)
import requests, subprocess, datetime

def days_left(domain):
    out = subprocess.check_output(
        ["openssl", "s_client", "-servername", domain,
         "-connect", domain + ":443"],
        stderr=subprocess.DEVNULL)
    # 解析 notAfter,计算剩余天数
    ...

def notify(text):
    requests.post("https://example-alert/api", json={"text": text}, timeout=5)

d = days_left("example.com")
if d < 14:
    notify(f"证书剩余 {d} 天,请确认自动续期是否正常")   # 提前预警
if d < 3:
    notify(f"证书剩余 {d} 天,即将过期!")              # 紧急告警

告警内容要带域名、带剩余天数、带下一步动作——"证书剩余 3 天"比"续期失败"有用得多,收到的人知道该去查什么。

告警通道也要有冗余:主通道(企业微信/钉钉群机器人)挂了,备用通道(短信)还能顶上。实践中见过主通道机器人 token 失效、告警静默一周的案例——所以监测任务本身要加一个"心跳":每天巡检成功与否都发一条极简消息,连续两天没收到,说明监测链路本身出问题了。

五、踩坑清单

  • 坑1:续期失败静默吞掉:cron 任务加 --quiet 且无日志,失败无人知。失败必须告警,不能只写日志。
  • 坑2:只查本机证书:本机证书文件是新的,CDN 节点/旧进程加载的还是旧证书。监测必须从外网视角查。
  • 坑3:服务器时间错乱:NTP 未同步导致续期请求被拒,证书默默过期。先保证 timedatectl set-ntp true。
  • 坑4:多域名/泛域名漏配:泛域名走 DNS challenge、单域名走 webroot,混合部署漏一条记录就"部分续期"。定期全量检查每个域名的剩余天数。
  • 坑5:续期后不 reload:证书更新后 nginx 不 reload,新证书不生效,看起来"续期成功"实则没换。续期脚本末尾加 nginx -t && nginx -s reload。

六、上线后的情况

改造后:证书剩余天数每天巡检一次,低于 14 天自动预警,续期失败直接告警到人;两次续期之间的时间偏差也被 NTP 同步解决。之后再没出现过"证书过期一周没人发现"的情况——浏览器安全警告从"事故"变成了"自动告警里的一个不会响的字段",官网的 HTTPS 状态终于进入了"被系统盯着"的状态。

结语

免费证书时代的坑不在证书本身,在"续期链路有没有监控"。到期监测管看见、自动续期管自愈、失败告警管行动、心跳巡检管链路自身——四段接上,SSL 证书才不会成为官网的定时炸弹。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 导读
  • 一、先说背景:证书过期一周,为什么没人发现
  • 二、到期监测:先知道"还有几天到期"
  • 三、自动续期:让续期链路本身可自愈
  • 四、失败告警:告警要可行动,不是一条日志
  • 五、踩坑清单
  • 六、上线后的情况
  • 结语
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档