当前位置:首页 >  IDC >  服务器 >  正文

SSL 证书过期与证书链不完整:openssl s_client 验证与续期监控

 2026-09-05 11:45  来源: 互联网   我来投稿 撤稿纠错

  一键部署OpenClaw

证书过期这事,一旦发生就是全站级别的红色警告。浏览器直接拦截,访客看到的不是你的网站,是一屏吓人的安全提示。更气人的是它完全可以避免——免费证书加自动续期,配好了就再也不用管。

Let's Encrypt 的证书有效期是 90 天,这个短期限是刻意设计的,目的是逼着大家把续期自动化。Certbot 是它官方推荐的客户端,装完之后会自带一个 systemd timer,每天检查两次,快到期的自动续。你要做的就是确认这个 timer 真的在跑。

这里有个时间点值得记住:从 4.0.0 版本起,Certbot 改成在证书剩余寿命不足三分之一时才真正续期,而不是固定提前 30 天。所以别看到它好几天没动作就以为坏了,那是正常行为。拿不准就用 --dry-run 跑一次演练,它会完整走一遍流程但不写真实证书,也不消耗申请配额。

另一类问题隐蔽得多:证书链不完整。服务器上只配了域名证书,没配中间证书,结果就是有的浏览器正常、有的报错。为什么会这样?因为现代浏览器会缓存中间证书,也会主动去拉取缺失的部分,所以你用自己电脑测可能完全正常,换个环境就挂了。安卓设备和一些老版本 OpenSSL 对这种情况特别敏感。

#!/bin/bash
# SSL 证书体检:链完整性 + 到期天数
DOMAIN="example.com"
PORT="443"
WARN_DAYS=20

# 1. 拉完整证书链,数一数有几层
CHAIN=$(echo | openssl s_client -connect "${DOMAIN}:${PORT}" \
-servername "${DOMAIN}" 2>/dev/null)
echo "$CHAIN" | grep -E "^ *[0-9]+ s:|^ *i:" | head -n 10

# 2. 链是否完整(Verify return code: 0 才是通过)
echo "$CHAIN" | grep -E "Verify return code"

# 3. 到期时间换算成剩余天数
END_STR=$(echo | openssl s_client -connect "${DOMAIN}:${PORT}" \
-servername "${DOMAIN}" 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
END_TS=$(date -d "$END_STR" +%s)
NOW_TS=$(date +%s)
LEFT=$(( (END_TS - NOW_TS) / 86400 ))

echo "证书到期:$END_STR"
echo "剩余天数:$LEFT"

if [ "$LEFT" -lt "$WARN_DAYS" ]; then
echo "警告:证书即将到期,请检查 certbot.timer"
systemctl is-active certbot.timer 2>/dev/null
fi

证书链不完整的修法通常是把 fullchain.pem 配上去,而不是 cert.pem。Certbot 签发的目录里这两个文件都在,前者包含了域名证书加中间证书的完整链条,后者只有域名证书。很多教程里图省事写 cert.pem,于是埋下这个雷。Nginx 配置里那行 ssl_certificate 后面跟的必须是 fullchain.pem。

# Nginx HTTPS 配置要点:注意是 fullchain 不是 cert
server {
listen 443 ssl http2;
server_name example.com;

# 必须是 fullchain.pem,只写 cert.pem 会导致证书链不完整
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;

# 反代场景下透传协议,否则程序会误判成 HTTP 导致重定向循环
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

续期之后还有一步容易漏:reload Web 服务。证书文件换了,但 Nginx 还握着旧的,不 reload 就不会生效。Certbot 的 deploy hook 就是干这个的,放在 renewal-hooks/deploy 目录下,续期成功自动触发。同样地,--dry-run 演练时 hook 也会执行,正好顺便验证它写得对不对。

最后提醒一个时间上的变化:Let's Encrypt 已经在推进更短的有效期,六天的短周期证书已经可以通过 shortlived 配置申请,按规划到 2028 年 2 月默认有效期会降到 45 天。这意味着自动续期从可选项变成必选项,任何依赖手工续期的站点都会被淘汰。现在把监控和自动化落地,后面就不用再操这份心。

相关阅读:证书问题是接入层故障的常客。《服务器故障排查总纲:按现象反查的五层定位法》把过期、链不完整、HTTPS 识别错乱归成一串,本篇管证书本身,总纲帮你把相邻故障一次看全。

申请创业报道,分享创业好点子。点击此处,共同探讨创业新机遇!

相关标签
网站服务器
ssl证书

相关文章

  • 进程被 OOM Kill:dmesg 定位与关键进程保护

    有一种故障特别难查:服务莫名其妙消失了,应用日志里什么都没留下,没有崩溃堆栈,没有异常退出记录。进程就这么凭空不见。碰到这种情况,十有八九是内核干的——内存不够了,OOMKiller挑中它杀了。OOM的全称是OutofMemory,它是内核的最后手段。Linux默认采用乐观的内存分配策略,进程申请内

  • Linux 磁盘满排查:df 到 du,定位大文件与日志轮转

    网站突然写不进数据,上传图片报失败,数据库开始拒绝写入。登上机器df一看,使用率100%。这种故障不复杂,但它有个特别坑的地方:你按常规思路用du去找大文件,把所有目录的大小加起来,发现远远小于磁盘用量。两个数对不上。对不上通常有三个原因。第一个最常见:文件被删了,但还被某个进程占着。Linux下删

    标签:
    网站服务器
  • MySQL 慢查询拖垮 CPU:processlist 到 pt-query-digest

    服务器CPU突然打满,网站慢得像拨号上网。登上机器一看,MySQL占了大头。这时候最忌讳的是直接重启数据库——重启完负载确实掉了,但过不了多久它还会爬回来,因为那条慢SQL还在那儿等着被执行。第一步是抓现行。SHOWPROCESSLIST能看到此刻正在跑的语句,但默认只显示前100个字符,长SQL会

  • MySQL 1040 Too many connections:连接泄漏定位与上限设定

    网站隔三差五报一句数据库连接失败,刷新两下又好了。这种时好时坏的毛病最烦人,因为它不像宕机那样干脆,你没法守在屏幕前等它出现。等到真去查的时候,现场早没了。MySQL从5.7起max_connections默认就是151,8.0也沿用了这个值。超过之后新连接直接被拒,报ERROR1040。看到这个错

  • PHP-FPM 进程耗尽:pm.max_children 用内存反推怎么算

    间歇性502里最磨人的一种:低峰期一切正常,流量一上来就崩,过会儿又自己好了。FPM日志里翻出serverreachedpm.max_children,基本就能确诊——子进程开到上限,新请求没进程可用了。很多教程看到这条日志就让你把pm.max_children调大,却不说调到多少。拍脑袋填个100

热门排行

信息推荐