朕喜技术(ZhenxiJS)
2026.09.01
1 分钟阅读
SSL certificate expired certificate has expired TLS 证书过期 openssl s_client Node.js HTTPS 自签名证书 curl cacert
Node.js HTTPS 服务 TLS 证书过期修复实战:SSL certificate expired 排查与解决
日期:2026-09-01 | 技术栈:Node.js / OpenSSL / TLS / HTTPS / Docker Compose 关键词:SSL certificate expired、certificate has expired、TLS 证书过期、openssl s_client、Node.js HTTPS、自签名证书、curl cacert
一、问题背景
维护的 Node.js HTTPS 服务突然无法访问,浏览器报「您的连接不安全 / 证书已过期」。但 netstat 显示 8443 端口正常监听,node 进程也在运行——服务进程没挂,只是 TLS 证书过期导致所有 HTTPS 客户端握手失败。
二、复现步骤
# 1. 启动服务环境
cd scenario/2026-09-01-0800
docker compose up -d --build
# 2. 检查服务器证书有效期(跳过信任校验)
echo | openssl s_client -connect localhost:8443 2>/dev/null | openssl x509 -noout -dates
# 输出:notBefore=Jan 1 00:00:00 2020 GMT
# notAfter=Jan 2 00:00:00 2020 GMT
# 3. 用 CA 证书完成信任校验 → 暴露证书过期
curl -v --cacert certs/ca.crt https://localhost:8443
# 报错:curl: (60) SSL certificate problem: certificate has expired
三、根本原因
服务器使用了一个有效期仅 1 天的已过期证书(2020-01-01 到 2020-01-02)。当客户端(curl/浏览器)建立 TLS 握手时,会校验服务器证书的有效期,发现 notAfter 时间已过去,直接拒绝连接。
服务进程本身没有崩溃,但 TLS 层无法完成握手,所以所有 HTTPS 请求都会失败。
为什么 curl 报两种不同的错误?
- 直接 curl 访问:报
unable to get local issuer certificate— 因为自签名 CA 不被系统信任。 --cacert指定 CA 后:报certificate has expired— 信任链能走了,但发现证书已过期。
四、解决办法
4.1 生成新的有效证书(365 天有效期)
# 创建 OpenSSL 配置,设置 default_days = 365
# 生成新的 CA 和服务器证书
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout ca.key -out ca.crt -days 3650 -subj "/CN=TestCA"
openssl req -new -newkey rsa:2048 -nodes \
-keyout server.key -out server.csr -subj "/CN=localhost"
openssl ca -config openssl.cnf -in server.csr \
-out server.crt -notext -batch
4.2 替换证书文件并重启服务
cp server.key server.crt ca.crt /workspace/certs/
docker compose restart
4.3 验证修复
echo | openssl s_client -connect localhost:8443 2>/dev/null | openssl x509 -noout -dates
# notBefore=Sep 1 00:07:49 2026 GMT
# notAfter=Sep 1 00:07:49 2027 GMT
curl -s --cacert certs/ca.crt https://localhost:8443
# Hello! This is an HTTPS service. If you can see this, the TLS handshake succeeded!
五、验证
- ✅
openssl s_client显示新证书notBefore=2026-09-01,notAfter=2027-09-01(有效期 365 天) - ✅
curl --cacert certs/ca.crt https://localhost:8443返回 200 OK - ✅ TLS 握手日志显示
SSL certificate verify ok.
六、经验与避坑
- 服务进程没挂 ≠ 服务可用:HTTPS 服务即使进程正常运行,证书过期后所有客户端都无法连接。监控时要同时关注证书有效期。
- Docker 容器中 node 是 PID 1:不能直接
kill -9node 进程,否则容器会退出。应使用docker compose restart。 - Node.js 不热加载证书:替换证书文件后必须重启进程,否则旧证书仍在内存中使用。
openssl s_client是排查 TLS 问题的利器:echo | openssl s_client -connect host:port 2>/dev/null | openssl x509 -noout -dates可快速查看服务器证书有效期。- 自签名证书需要
--cacert指定 CA:否则 curl 先报unable to get local issuer certificate,掩盖了真实的证书过期问题。 - 生产环境应使用证书自动续期:如 Let's Encrypt + certbot,或用 ACME 协议自动管理证书,避免手动操作遗漏导致证书过期事故。
可复现命令
git checkout 2026-09-01-0800
cd scenario/2026-09-01-0800
docker compose up -d --build
echo | openssl s_client -connect localhost:8443 2>/dev/null | openssl x509 -noout -dates
curl -v --cacert certs/ca.crt https://localhost:8443
