最近给几个自用 Web 服务排了两次很像、但根因完全不同的 “网页偶发卡几秒”,过程挺有意思,脱敏记录一下,也想问问大家有没有遇到类似情况。
环境大致是:自建源站 + Caddy,前面还有 TCP 四层分流;手机 Android/Chrome 是主要访问端。下面不放域名、IP 和具体机器。
第一类:Cloudflare Tunnel 路径卡顿
之前一部分服务的数据链路是:
浏览器 -> Cloudflare/Access -> Tunnel -> 源站
在某些手机网络 / Wi-Fi 下,经常出现首开转圈几秒,而且刷新也会继续卡,不是 “第一次慢、第二次快”。源站负载、源站本地访问都正常,同一设备换网络后体感又可能明显不同。
后来做 A/B,把数据面从 Tunnel 拿掉,只保留需要的鉴权能力,页面 /API/SSE 改成直接访问源站。那一类卡顿明显改善。
所以这次的经验是:Tunnel 本身不等于慢,但如果某条本地网络到 CF 边缘 / Anycast 的路径不理想,它会成为所有请求共同经过的瓶颈。这个场景里 “刷新也卡” 是一个比较明显的特征。
第二类:直连以后,又遇到一种 “冷开卡 5~10 秒,刷新秒开”
这次已经不是 Tunnel 了。现象是:
- 页面放一阵子不访问,再打开,偶尔空白 / 转圈 5~10 秒;
- 一旦出来,马上刷新就是秒开;
- 随后访问同域名其他页面也很快;
- 关掉 Clash/TUN 后仍然可以复现。
一开始很容易怀疑前端,所以我也走了不少弯路:拆大 JS、拆大 CSS、做 critical CSS、把非关键库延后、记录 Long Task 等。确实顺手把页面从 500KB+ 的大单文件整理成了小 HTML + 可缓存 CSS/JS,但后来证明这些不是那 5~10 秒的根因。
真正有用的是逐层做 A/B:
- 加 Navigation Timing / Resource Timing;
- 做一个同域名、只有 1~2KB 的极简 cold-test 页面;
- 关掉 Clash/TUN 测;
- 源站持续做 443 环形抓包。
极简页一样会冷开卡几秒,而且刷新秒开,于是前端复杂度基本出局。
抓到的一次典型样本里,浏览器总等待接近 10 秒,但最终成功的新 TCP connect 只有一两百毫秒,TLS/TTFB 也正常。也就是说,真正慢的不是 “新连接建立以后”,而是新连接真正建立之前的那段时间。
更有意思的是旧 HTTP/2 连接的生命周期:服务器原来使用 Caddy 默认大约 5 分钟的 HTTP idle timeout。抓包看到一条旧 h2 连接空闲到约 5 分钟时,服务器开始正常发关闭包,但手机侧没有正常回应,服务端随后还能看到关闭包重传。
这里我不敢直接下结论说 “一定是某个运营商 CGNAT 在第 X 分钟把映射删了”,因为空闲期间没有探测包,没法知道链路究竟在哪一秒失效;也可能涉及 Android/Chrome 的连接池、网络切换 / 省电状态或中间 NAT / 防火墙。
但 A/B 很明确:把 Web 侧 Caddy 的 HTTP idle timeout 从默认 5 分钟缩到 30 秒后,让长期不用的连接早点正常退休,后续多次冷开都直接新建连接,原来 5~10 秒的黑洞目前完全消失。
现在的策略相当于:页面加载时照常享受 HTTP/2 复用;连接连续空闲 30 秒就由服务器主动结束,避免客户端过几分钟还抱着一条可能已经不可用的旧连接。
这两次问题虽然表面都是 “网页偶发卡几秒”,但特征其实完全不同:
- Tunnel / 路径问题:首开卡,刷新通常也卡;换网络 / 改直连路径影响明显。
- 空闲 h2 旧连接问题:放一阵后第一次卡,刷新立即秒开;同源极简页也能复现。
顺便一个教训:只看 connect/TLS/TTFB 的 “持续时间” 有时会误判,因为它们描述的是最终成功阶段,不一定把前面尝试复用旧连接、等待失败的时间直观展示出来。浏览器 Navigation Timing + 服务端 tcpdump 对时间轴,比单看服务器日志好用得多。
想请教下大家:
- Android Chrome + 移动网络 / CGNAT 下,有没有遇到过类似 stale HTTP/2 connection / idle connection reuse 的现象?
- 自建 Caddy/Nginx/HAProxy 的 Web 服务,大家会主动把 HTTP idle timeout 设短一些吗?30s、60s 还是更长?
- 有没有更优雅的做法,比如 TCP keepalive / HTTP2 PING,既保留长连接又避免这种黑洞等待?
- 如果你们也碰到 “冷开卡、刷新秒开”,一般第一时间会抓哪几个指标?
纯技术排障交流。
- 收藏支持反对打赏
感觉交给 AI 能帮你快速定位
不知道,蹲后续