可用shell脚本结合openssl和curl批量检查nginx线上站点证书有效期:先从nginx配置提取https域名与端口,再用openssl s_client获取并解析叶子证书的notbefore/notafter时间,最后对比当前时间输出过期、预警或正常状态。

<p>可以用 Shell 脚本结合 <code>openssl</code> 和 <code>curl</code> 批量检查 Nginx 线上所有站点的证书链有效期。核心思路是:从 Nginx 配置中提取 server_name 和监听端口(含 HTTPS),对每个域名+端口发起 TLS 握手,获取并验证证书链中**叶子证书**的有效期(通常只需关注终端证书,中间证书一般远长于叶子证书)。</p> <h3>1. 提取 Nginx 配置中的 HTTPS 站点</h3> <p>遍历 <code>/etc/nginx/sites-enabled/</code> 或主配置包含的 conf 文件,找出启用 SSL 的 <code>server</code> 块及其域名、端口:</p>
- 匹配
listen *:443 ssl;或listen 443 ssl; - 提取
server_name后的域名(支持多个,空格或分号分隔) - 跳过注释行和 include 行,忽略纯 IP 监听或无 server_name 的配置
示例命令片段(用于生成待测域名列表):
nginx -t &>/dev/null || { echo "Nginx config invalid"; exit 1; }
grep -r "listen.*443.*ssl" /etc/nginx/sites-enabled/ --include="*.conf" | \
awk -F':' '{print $1}' | sort -u | \
while read conf; do
awk '/^[[:space:]]*server[[:space:]]*{/,/^[[:space:]]*}/ {
if (/^[[:space:]]*listen[[:space:]]+[0-9]+:[[:space:]]*443[[:space:]]+ssl/) ssl=1;
if (/^[[:space:]]*server_name/ && ssl) {
gsub(/;/,"",$0); gsub(/server_name[[:space:]]+/,"",$0);
for(i=1;i
2. 对每个域名执行证书有效期检查
使用 openssl s_client 连接 443 端口(可加 -servername 支持 SNI),提取 PEM 格式证书链,再用 openssl x509 解析第一张(叶子)证书的生效与过期时间:
- 超时设为 5 秒(
-timeout 5),避免卡住 - 忽略验证错误(
-verify_return_error不启用),只关心能否拿到证书 - 用
awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/'提取首张证书 - 用
openssl x509 -noout -dates输出notBefore和notAfter
关键检查逻辑(单域名):
domain="example.com"
if ! cert=$(echo | openssl s_client -connect "$domain:443" -servername "$domain" -timeout 5 2>/dev/null | \
sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' | head -n +200); then
echo "$domain: connect failed or no cert"
continue
fi
if ! not_after=$(echo "$cert" | openssl x509 -noout -enddate 2>/dev/null | cut -d'=' -f2-); then
echo "$domain: invalid cert format"
continue
fi
# 转为秒级时间戳比较(Linux GNU date)
expire_ts=$(date -d "$not_after" +%s 2>/dev/null)
now_ts=$(date +%s)
if [ -z "$expire_ts" ] || [ "$expire_ts" -lt "$now_ts" ]; then
echo "$domain: EXPIRED or parse failed"
elif [ "$expire_ts" -lt $((now_ts + 30*86400)) ]; then
echo "$domain: expires in $(($(($expire_ts - $now_ts)) / 86400)) days ⚠️"
else
echo "$domain: OK ($(($(($expire_ts - $now_ts)) / 86400)) days left)"
fi
3. 完整脚本要点(可直接保存运行)
整合上述逻辑,加入错误处理、去重、跳过内网/localhost 域名,并支持自定义端口(如 8443):
- 先用
nginx -T导出全部生效配置,避免手动找路径遗漏 - 用
grep -v "127.0.0.1\|localhost\|::1"过滤本地测试域名 - 对每个域名尝试
dig +short A $domain | head -1确保 DNS 可解析(可选) - 输出 CSV 或着色文本,便于人工或后续告警(如发邮件给运维)
- 建议搭配 cron 每周执行一次:
0 2 * * 1 /path/to/check-certs.sh >> /var/log/cert-check.log 2>&1
4. 注意事项
真实环境需考虑:
-
多端口:若站点用非标准 HTTPS 端口(如 8443),需在提取配置时一并捕获
listen 8443 ssl并记录端口 - SNI 必须开启:OpenSSL 默认发 SNI,但某些老旧 Nginx 或 CDN(如 Cloudflare)可能返回默认证书,需确认是否符合预期
-
证书链完整性:该脚本只校验叶子证书;若需检查整个链(如中间 CA 是否缺失),可用
openssl s_client -showcerts+ 多次x509 -noout -text分析,但实际运维中叶子证书过期才是主要风险点 -
权限问题:脚本应以能读取 Nginx 配置的用户(如 root 或 nginx 组)运行;生产环境不建议直接用 root,可用
sudo -u nginx限定权限











