curl访问HTTPS报28 Operation timed out:连接超时与总超时分层排查

HTTPS检查突然报curl: (28) Operation timed out,日志前面却写着Connected。证书刚换过,很容易顺手把锅甩给它。但TCP接通不是TLS握手完成,TLS完成也不是HTTP响应到齐。先找出超时发生在哪一段,再决定查网络、握手还是应用,别让重签证书变成安慰剂。

一、28说明超时,不直接说明证书坏了

curl错误28表示操作超时。它并不指定唯一根因:名称解析、连接、TLS握手、等待响应或传输过程都可能用尽预算。证书或对端身份验证失败通常对应60,但最终错误取决于先发生的失败;如果握手还没收到足够数据,不能要求工具先给你一个证书结论。

本文讨论单次、新建、直连的HTTPS请求,不带重试、跳转与连接复用。实验使用本机curl 7.61.1、OpenSSL 1.1.1k和回环临时服务,未测试公网链路,未改生产配置。先记录实际版本,不能把网上最新版选项当作本机已有。

bash 复制代码
curl --version

二、connect-timeout不只管TCP

官方手册将连接阶段定义为DNS查询及所需TCP、TLS等握手完成之前。对这里的HTTPS请求,connect-timeout覆盖TLS握手;max-time限制整次传输,连接阶段也包含在内。两个值不是可以相加的额度,也不是先用完连接预算,再另领一份总预算。

图只画适用范围,不表示每段必然耗时相同,也不是HTTP/3或代理链路的完整时序。TCP已建立而TLS停住时,connect-timeout仍可能到期;TLS完成以后慢慢等HTTP,连接超时就不是这段等待的截止线。

三、保留阶段计时,不只看最后一句

下面是诊断模板,example.com须替换为获准测试的目标。0.5秒和2秒用于说明,不是生产推荐阈值。-q必须作为第一个选项用于忽略默认配置文件;它不清空所有环境变量。--noproxy表示本次直连,--http1.1让协议边界更容易观察;必要代理环境不要照抄直连假设。

bash 复制代码
curl -q --noproxy '*' --http1.1 -sS \
  --connect-timeout 0.5 --max-time 2 \
  -o /dev/null \
  -w 'tcp=%{time_connect} tls=%{time_appconnect} first=%{time_starttransfer} total=%{time_total} http=%{http_code}\n' \
  https://example.com/

time_connect、time_appconnect、time_starttransfer与time_total是从开始计算的累计时刻,不是四段互不重叠的耗时。对本例可用TLS完成时刻减TCP完成时刻观察握手区间,但有重定向、代理、复用时不能照搬这套减法。

字段为0可能表示对应阶段没有完成,不能仅凭它断定"TLS耗时为零"。http_code为000也不是服务器发送的HTTP状态码;它说明这里没有取得可报告的响应码。结合stderr和服务端事件,才能定位到具体阶段。

四、用两种停顿拆开同一个28

隔离对照一:服务接收TCP,但不回应TLS。客户端记录到TCP完成,TLS完成时间仍为0,在约0.5秒报28;服务端没有HTTP请求事件。这里连接已经"接上",连接阶段却没有完成。

隔离对照二:临时测试CA受信、名称正确、TLS正常完成,服务读取HTTP请求后暂不返回响应。同一命令在约2秒报28,TLS计时非零、尚无首字节。只盯着28看不出区别,服务端已收到请求这个证据很重要。

隔离条件 退出码 观察重点
TCP接受后TLS停顿 28 连接阶段预算到期
TLS完成后HTTP停顿 28 总传输预算到期
快速响应且信任正确 0 仍需核对HTTP与业务
正常握手但测试CA不受信 60 没有进入HTTP请求

五、总超时更短时,谁先到就先停

把两个值交换,可以检查"连接超时更长就一定能等更久"的误解。TLS停顿夹具在总预算约0.5秒时结束,不会因为连接允许2秒就继续等。

bash 复制代码
curl -q --noproxy '*' --http1.1 -sS \
  --connect-timeout 2 --max-time 0.5 \
  -o /dev/null -w 'total=%{time_total} http=%{http_code}\n' \
  https://example.com/

这些近似时间不是硬实时承诺。系统调度、解析器与运行环境都会影响观测,应保留实际值而不是断言小数点后完全相等。本文不启用--retry;启用重试后max-time会在重试时重置,整个任务还需要独立重试预算和调用次数上限。

六、把退出码与证据一起留下

把下面保存为check-timeout.sh,执行bash check-timeout.sh https://example.com/。脚本保留本次日志目录,TMPDIR须存在且可写,事后按本机保留策略清理。它不输出私钥或认证头;实际URL仍应避免携带Token与个人资料。

bash 复制代码
#!/usr/bin/env bash
set -u
[[ $# == 1 ]] || exit 2
umask 077
work=$(mktemp -d "${TMPDIR:-.}/https-timeout.XXXXXX") || exit 2
curl -q --noproxy '*' --http1.1 -sS \
  --connect-timeout 2 --max-time 5 \
  -o /dev/null \
  -w 'tcp=%{time_connect} tls=%{time_appconnect} first=%{time_starttransfer} total=%{time_total} http=%{http_code}\n' \
  "$1" >"$work/timing.txt" 2>"$work/error.txt"
rc=$?
printf 'curl_exit=%s evidence=%s\n' "$rc" "$work"
exit "$rc"

立即保存curl退出码,别先echo再取$?。这里不用set -e,因为失败后还要记录结果;也不接tee管道,避免误拿日志程序的退出码。本轮已从最终HTML提取四块命令,在回环夹具执行正常、TLS停顿、HTTP停顿与不受信对照;临时私钥、监听和测试日志已清理。

七、修复按层做,验收不能只延长时间

  • 保留版本、目标、超时参数、退出码及采样时间。
  • 握手前停住,结合解析、网络与TLS端日志查路径,不凭Connected放行。
  • 握手后停住,核对应用是否收到请求、首字节和响应体进度。
  • 证书失败按链、名称与时间修复,不使用-k绕过,也不把调大超时当作信任修复。
  • 最终重新验证TLS、HTTP状态及业务内容;0退出码本身不是业务成功。

客户端超时只是停止等待,不保证服务端没有执行请求。涉及写入时,先核对服务端结果和业务幂等设计,再决定重试;诊断脚本不能证明回滚成功。

八、参考资料

相关推荐
曦夜日长1 小时前
主从 Reactor 高并发服务器实战 (一):前置知识与功能目标
运维·服务器
2601_965619691 小时前
历史解说视频怎么做:青铜器瓷器文物画面自动对齐的完整教程
运维·nginx·音视频
LiaoWL1232 小时前
从零开始学 Linux(二):Shell 脚本入门与文本处理实战
linux·运维·服务器
.道阻且长.2 小时前
Linux软件包管理器
linux·运维·ubuntu·职场和发展·centos
元岳数字人小元2 小时前
一台终端兼顾接待与咨询,AI数字人对话赋能线下多场景服务
运维·人工智能·人机交互·交互·语音识别·源代码管理
djjjx.2 小时前
Linux进程篇(三):深度硬核!全面起底 Linux 进程状态变化与内核链表动态解绑
linux·运维·服务器
qqxiaoxingxing11 小时前
HR重复性工作自动化落地指南:先梳理任务边界,再选工具
运维·自动化
王琦031812 小时前
持续集成持续交付企业示例
运维·ci/cd