HTTPS证书过期了?我翻车后总结的解决方案
凌晨两点,手机震了——"您的网站证书已过期"。爬起来一看,用户访问直接报安全警告,流量腰斩。那一刻我意识到,HTTPS证书这玩意儿,不出事不知道,一出事要命。今天把这次翻车从头到尾捋一遍,顺手把SSL/TLS/CA的原理、自动续期、常见报错全讲透,帮你别踩我踩过的坑。
一、翻车现场还原:那天晚上到底发生了什么
事情是这样的——我负责的一个外部API服务,三个月前用 Let's Encrypt 申请了证书,当时图省事,手动续期了一次就没再管。结果三个月后证书悄无声息地过期了,用户调用接口直接收到浏览器的"连接不安全"提示。客服反馈过来的时候我正在打游戏,一打开后台日志,大片 525 错误(SLL handshake failed),当时心态就崩了。
简单复盘一下当时的惨状:
- 证书过期后,用户浏览器直接拦截请求,显示"您的连接不是私密连接"
- APP 里的 HTTP Client 报
ssl: certificate_verify_failed - 搜索引擎把 site 标记为"不安全",SEO 排名肉眼可见地掉
这次翻车让我彻底明白了一件事:证书管理这事,靠手动续期等于裸奔,自动续期才是正道。下面把从原理到实操的全部经验整理出来。
二、SSL/TLS/CA 原理:弄清楚证书到底是什么
2.1 对称加密与非对称加密
在说 SSL 证书之前,先把加密基础过一遍。对称加密就是加密和解密用同一把钥匙,速度快,但钥匙怎么安全地给对方是个问题。非对称加密则是一对钥匙——公钥和私钥,公钥加密的内容只有私钥能解,私钥签名的东西只有公钥能验证。SSL/TLS 用的是混合方案:握手阶段用非对称加密协商出一个对称密钥,之后的数据传输全用对称加密,兼顾安全性和性能。
2.2 证书是什么
SSL 证书(也叫 TLS 证书)本质上是一个数字文件,里面包含了网站的公钥、域名信息、颁发者、有效期,还有一个数字签名。浏览器拿到证书后,用内置的 CA 根证书验证签名是否合法,验证通过说明"这个公钥确实属于这个域名",否则就是中间人攻击。
一个完整的证书链是这样的:
根CA证书(Root CA)
└─ 中间CA证书(Intermediate CA)
└─ 服务器证书(Server Certificate)← 你网站用的
光有服务器证书不够,必须整条链都能被验证,浏览器才会信任你。很多时候证书不认,不是服务器证书的问题,而是中间证书没配对。
2.3 CA 的角色
CA(Certificate Authority,证书颁发机构)就是那个负责"认证"域名身份的可信第三方。国际上常见的 CA 有 Let's Encrypt、DigiCert、GlobalSign、 Sectigo 等。Let's Encrypt 是目前最流行的免费 CA,由 ISRG 运营,自动化程度极高,几乎是个人站点的标配。
2.4 证书类型:DV、OV、EV
- DV(Domain Validation):只验证域名所有权,几分钟就能签发,适合个人站点和博客
- OV(Organization Validation):验证域名所有权 + 企业身份,证书里会显示公司名,适合企业级应用
- EV(Extended Validation):最严格的验证,浏览器地址栏会显示绿色企业名称,适合金融、电商等高信任需求场景
我们普通开发者用 Let's Encrypt 申请的,基本都是 DV 证书,足够用了。
三、用 openssl 检测证书是否过期
这是运维基本功,openssl 一把梭就能查清楚。
3.1 查看证书详细信息
openssl s_client -connect www.example.com:443 -servername www.example.com 2>/dev/null | openssl x509 -noout -text
这条命令连到目标网站的 443 端口,把证书拿出来解析。-servername 参数不能省,因为很多站点是 SNI(Server Name Indication)场景下跑多个域名,不加这个拿到的可能是默认证书而不是你想要的。
输出里重点关注这几个字段:
Not Before: Apr 15 00:00:00 2026 GMT
Not After : Jul 15 00:00:00 2026 GMT
Issuer: C=US, O=Let's Encrypt, CN=R10
Subject: CN=www.example.com
Not After 就是过期时间,Not Before 是生效时间,两个都在有效期内证书才能用。
3.2 直接检查过期时间(最常用)
echo | openssl s_client -connect www.example.com:443 2>/dev/null | openssl x509 -noout -dates
输出简洁:
notBefore=Apr 15 00:00:00 2026 GMT
notAfter=Jul 15 00:00:00 2026 GMT
3.3 批量检查本地证书文件
如果你的证书在服务器上存着(常见路径:/etc/ssl/certs/、/etc/nginx/ssl/、/var/www/ssl/),直接读文件检查:
openssl x509 -in /path/to/cert.pem -noout -dates -subject
结合 date 命令可以写一个简单脚本,判断证书是否在 N 天内过期:
#!/bin/bash
CERT="/path/to/cert.pem"
DAYS=30
END_DATE=$(openssl x509 -in "$CERT" -noout -enddate | cut -d= -f2)
END_EPOCH=$(date -d "$END_DATE" +%s)
NOW_EPOCH=$(date +%s)
LEFT=$(( (END_EPOCH - NOW_EPOCH) / 86400 ))
if [ $LEFT -lt $DAYS ]; then
echo "⚠️ 证书还有 ${LEFT} 天过期,请尽快续期!"
else
echo "✅ 证书还有 ${LEFT} 天过期。"
fi
3.4 检查证书链完整性
openssl s_client -connect www.example.com:443 -showcerts 2>/dev/null
这条命令会输出完整的证书链,检查是否有缺失的中间证书。如果链不完整,浏览器会报 ERR_CERT_AUTHORITY_INVALID 错误。
四、用 curl 验证 HTTPS 连接
有时候你不需要看证书详情,只需要确认"这个站能不能正常 HTTPS 访问"。
4.1 基础连接测试
curl -v https://www.example.com/
-v 会输出详细的 SSL 握手信息,包括用的 TLS 版本、cipher 套件、证书 Subject 等。正常情况下结尾会显示 HTTP/2 200 或者你站点的状态码。
4.2 检查证书过期警告
如果你怀疑证书过期,可以用 --cacert 指定 CA 证书文件,或者直接让 curl 跳过证书验证来排查是不是证书问题:
# 临时跳过证书验证(仅测试用!)
curl -k https://www.example.com/
# 对比:正常验证模式
curl https://www.example.com/
加了 -k(--insecure)之后跳过证书验证,如果加了 -k 能通但不加不能通,那基本就是证书的问题。注意这个参数只用于诊断,生产环境绝对不能依赖它。
4.3 检查 TLS 版本
curl -v --tlsv1.2 https://www.example.com/
确保站点支持 TLS 1.2 及以上,TLS 1.0 和 TLS 1.1 已经基本被主流浏览器废弃,Chrome 从很早就不支持了。
4.4 用 curl 检查证书链各层级
openssl s_client -connect www.example.com:443 -servername www.example.com -showcerts 2>/dev/null | \
awk '/-----BEGIN CERTIFICATE-----/{p=1} p; /-----END CERTIFICATE-----/{p=0}' | \
openssl verify -CAfile /dev/null -partial_chain 2>&1
这条稍微复杂,核心思路是:先把证书链提取出来,再逐级验证。如果中间证书缺失,这一步会报错。
五、自动续期:Let's Encrypt + acme.sh
手动续期的尽头一定是忘记续期。Let's Encrypt 的证书有效期是 90 天,我建议用 acme.sh 做全自动续期——轻量、支持中文、用 DNS API 泛解析方式验证,一次配好一年无忧。
5.1 acme.sh 安装(一条命令)
curl https://get.acme.sh | sh -s email=your@email.com
安装完成后重新加载 shell 环境,或者直接 source:
source ~/.acme.sh/acme.sh.env
5.2 申请证书(DNS API 方式,最通用)
以 Cloudflare 为例,先去 Cloudflare 申请 Global API Key,然后:
export CF_Key="your_cloudflare_api_key"
export CF_Email="your@email.com"
acme.sh --issue --dns dns_cf -d example.com -d "*.example.com"
用泛域名证书 *.example.com 可以覆盖所有子域名,一次申请全部搞定。dns_cf 表示用 Cloudflare 的 DNS API 自动添加 TXT 记录完成域名验证,全过程无需人工干预。
其他主流 DNS 服务商的 API 方式:
- 阿里云:
--dns dns_ali,需要Ali_Key和Ali_Secret - 腾讯云:
--dns dns_dp,需要DP_Id和DP_Key - 华为云:
--dns dns_huaweicloud
5.3 Nginx 自动部署
申请完证书之后,直接用 --install-cert 把证书和 key 路径写入 Nginx 配置,并设置好续期后自动 reload:
acme.sh --install-cert -d example.com \
--key-file /etc/nginx/ssl/example.com.key \
--fullchain-file /etc/nginx/ssl/example.com.cer \
--reloadcmd "systemctl reload nginx"
这条命令会把配置写入 ~/.acme.sh/example.com/,续期后自动执行 reloadcmd,不用你再手动重启 Nginx。
5.4 手动续期测试
acme.sh --renew -d example.com --force
加 --force 可以强制立即续期(一般用于测试)。正常情况下 acme.sh 每天会自动检测快过期的证书,在过期前 30 天自动续期。
5.5 查看已安装的证书
acme.sh --list
输出包含域名、证书路径、剩余天数,一目了然。
5.6 续期日志
tail -f ~/.acme.sh/acme.sh.log
如果续期失败了,第一时间看日志,常见原因:DNS API Token 失效、80/443 端口被占用、Let's Encrypt API 限流。
六、Nginx / Apache 配置完整示例
6.1 Nginx 配置
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# 证书和私钥路径(acme.sh 安装后自动生成)
ssl_certificate /etc/nginx/ssl/example.com.cer;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
# 现代 TLS 配置
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;
ssl_prefer_server_ciphers off;
# HSTS(可选,加了之后浏览器会强制 HTTPS,建议稳定后再开)
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
root /var/www/html;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
}
# HTTP 强制跳转 HTTPS
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
6.2 Apache 配置
<VirtualHost *:443>
ServerName example.com
DocumentRoot /var/www/html
SSLEngine on
SSLCertificateFile /etc/apache2/ssl/example.com.cer
SSLCertificateKeyFile /etc/apache2/ssl/example.com.key
# Let's Encrypt 中间证书(如果需要)
SSLCertificateChainFile /etc/apache2/ssl/fullchain.cer
Protocols h2 http/1.1
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
</VirtualHost>
配置完成后记得测试再重载:
# Nginx
nginx -t && systemctl reload nginx
# Apache
apachectl configtest && systemctl reload apache2
七、常见报错大全
7.1 ERR_CERT_DATE_INVALID / 证书已过期
原因:证书的 Not After 时间已过,或者客户端系统时间错误。
解决:
- 用 openssl 检查证书实际过期时间:
openssl x509 -in cert.pem -noout -enddate - 确认服务器系统时间正确:
date && timedatectl - 如果是自己生成的测试证书(自签名),浏览器不认是正常现象,换正式证书即可
7.2 ERR_CERT_AUTHORITY_INVALID / 证书链不完整
原因:服务器只配了服务器证书,没有配中间 CA 证书,浏览器找不到可信的签发者。
解决:
- 用
openssl s_client -connect yoursite:443 -showcerts导出完整链 - 把中间证书拼接到服务器证书后面,保存为
fullchain.cer - Nginx 中使用
ssl_certificate指向 fullchain,ssl_certificate_key指向私钥
7.3 SSL: certificate_verify_failed(Python / curl)
原因:Python 的 requests 库或 curl 找不到受信任的 CA 证书来验证服务器证书。
排查步骤:
# 确认系统 CA 证书包存在
ls /etc/ssl/certs/ca-certificates.crt
# Python 里指定 CA 文件
requests.get('https://example.com/', verify='/etc/ssl/certs/ca-certificates.crt')
# 或者用 certifi 获取默认 CA 包
import certifi
requests.get('https://example.com/', verify=certifi.where())
7.4 acme.sh: Please point correctly / DNS 验证失败
原因:DNS API Token 权限不足或已失效,acme.sh 无法自动添加或删除 DNS TXT 记录。
解决:
- 确认 API Token 有修改 DNS 记录的权限
- 检查 DNS 生效时间(DNS 变更有传播延迟,通常 1-5 分钟)
- 申请泛域名证书时需要支持 wildcard 的 API 权限
- 改用手动 DNS 验证方式(
--dns --yes-I-know-dns-manual-mode-enough-go-ahead-please)做临时调试
7.5 Let's Encrypt rate limit exceeded
原因:Let's Encrypt 对每个域名每周有申请次数限制(50 个),短时间内重复申请会触发限流。
解决:
- 等待一周后重试(限流自动解除)
- 用
--staging参数先在测试环境申请,不占生产限额 - 检查 acme.sh 是否有重复的申请记录,清理
~/.acme.sh/里的废弃证书
7.6 Nginx 重载后 HTTPS 不生效
排查顺序:
nginx -t— 语法是否正确systemctl status nginx— 服务是否正常运行ss -tlnp | grep :443— 443 端口是否在监听- 看 Nginx error log:
tail /var/log/nginx/error.log - 确认证书路径文件存在:
ls -la /etc/nginx/ssl/
八、证书监控:别等翻车了才想起来
翻车一次就够了,生产环境强烈建议上监控。几个方案:
- acme.sh 自带通知:续期前 7 天会自动发邮件提醒
- 定时扫描脚本:每天跑一次,把快过期(<30 天)的域名捞出来推送到飞书/钉钉
- 监控平台:用 Prometheus + blackbox_exporter 监控 HTTPS 可用性和证书过期时间
顺手推荐一个在线工具,可以快速检测任意网站的 SSL 证书状态,无需注册:
👉 SSL 证书在线检测工具 — 支持查看证书链、过期时间、TLS 版本、 cipher 套件,运维日常神器。
总结
HTTPS 证书过期这事,说大不大说小不小——对于一个访问量可观的线上服务而言,就是一次中型故障。核心经验就三条:
- 自动化续期是必选项,不是可选项。Let's Encrypt + acme.sh 配好之后真的不用管。
- 证书链要配完整,服务器证书 + 中间证书一个不能少,否则部分客户端会验证失败。
- 做好监控告警,别等用户报障了你才知道证书过期了。
好了,老司机经验全部交代完毕。如果你觉得有用,欢迎收藏。需要配置 HTTPS 或者排查具体报错,可以去 SSL 检测工具 先跑一遍,看看问题出在哪一环。祝你的证书永远不过期,凌晨两点不被叫醒。🛡️
常见问题
A: 这类工具一般有明确的输入框和输出框,按提示输入内容,点击对应按钮即可得到结果。建议先用简单示例测试功能是否正常,再处理实际数据。
A: 根据具体工具类型决定。格式转换工具适合处理第三方数据,编码工具适合加密传输,压缩工具适合文件上传前处理。多积累工具使用经验,遇到问题时能快速判断用哪个工具解决。
A: 不同工具有不同侧重,重点是理解原理。可以同时安装多个类似工具,实际使用中对比效果,选择最顺手的一个。随着使用经验增加,你也能判断工具的好坏。