标签页上还停在隧道断开前的那个面板:状态数字冻住了,小转圈还在转,地址栏里仍是你一小时前打开的应用。断开之后你什么都没碰,但页面也许仍按自己的节奏去够它的服务器。这正是人们容易漏掉的地方:只是看起来还在的页面,和仍在发请求的页面,从外面看一模一样。网络日志里只会出现后者。

页面自己会开始的工作

有四种机制会让已加载的文档持续忙碌。重复定时器可以每隔几秒重跑一次状态查询。切回标签页、页面重新可见时,fetch 可能被触发。基于 WebSocket 或服务端推送事件的长连接,会按自己的退避节奏尝试重连。网站注册的 service worker,甚至在你面前没有任何标签页时,也会因为排队中的同步而醒来。

这些都不需要点击,也都不会重新加载文档。地址栏、滚动位置和已渲染的数值全都留在原处。变的只有网络这一侧:每一次新尝试,都是它开始那一刻作出的新决定。

这个区别重要,因为可见页面是存在本地的。已经画出来的像素,说明不了下一个请求有没有成功。带转圈的冻结图表,正是请求开始失败时页面的常见样子。读屏只能告诉你服务器几分钟前说了什么,而不是浏览器此刻在做什么。

为什么先前的页面加载保不住一条路由

页面加载里没有任何东西把文档绑在隧道上。路由是在发包那一刻,按每个数据包用当时的表解出来的。隧道接口消失后,这个查表就不再命中隧道路由。它要么漏到物理网卡上,要么什么都查不到,取决于客户端留下了什么。

重复回调只是普通的计划任务。HTML 标准中讲定时器的那一节说明了它们如何排队,并把间隔当作最小延迟,而不是精确节奏的承诺,所以后台标签页轮询的次数可以远少于代码要求。尝试变少不等于不再尝试,而把标签页切回前台,往往就是它们集中爆发的时候。

失败策略落在哪一层

客户端的封锁是在尝试建立连接时判定的,不是页面被取回的时候。如果普通访问仍被允许,重试就会以明文从物理链路出去。如果策略把这份流量扣到隧道恢复为止,尝试就会在连接那一刻失败,页面显示一个错误,几秒后可能再重试。

有两个细节决定你落在哪种情况。第一是客户端支持的执行范围:整台设备的封锁、只限隧道网卡的封锁,或带排除项的规则集。第二是目的地是否落在此前为本地打印机、强制门户或流媒体服务添加的排除项里。

应用层加密不会改变这些。HTTPS 保护的是浏览器与服务器之间的字节,并不说明这些字节走哪块网卡。页面可以加密得很完整,却仍然从错误的路径出去,而窄封锁允许的恰恰就是这一点。

用一次性页面做受控检查

用一个你自己控制的宿主,和一个除了调用它之外什么都不做的页面,这样就没有真实数据参与。在终端里,采样断开前后的路径:

curl -sS --max-time 5 -o /dev/null -w '%{http_code} %{remote_ip}\n' https://probe.example.net/ping

先在隧道开启时跑一次,再把隧道关掉跑一次。隧道关着的时候,如果还能从公网地址拿到回应,说明直连路径是通的。超时或连接被拒绝,说明策略正在扣住。客户端重连之后再重复一次,亲眼看到隧道路径回来,而不是想当然。

在浏览器里,网络面板能把这两个问题分开。筛到 Fetch/XHR,然后看启动者那一列:由定时器或脚本发起、之前没有导航记录的条目,是后台尝试而不是重新加载。把它的开始时间和隧道断掉的时刻对齐。如果隧道已经没了而条目还挂着,这个请求就在等一条不存在的路。

服务器端的证据比浏览器显示的任何东西都硬。带时间戳和来源地址的请求日志能证明数据包到了。浏览器条目可能带着 Cookie、令牌和完整 URL,所以只保留你这个问题需要的字段,其余的在贴到任何地方之前先遮掉。

拿到答案之后做什么

如果隧道关着时后台尝试还在成功,说明封锁比你以为的更窄,在把敏感工作托付给客户端之前,这个缺口值得补上。如果它们失败了,说明策略在起作用,冻结的页面只是表象。无论如何,关掉一个标签页都不是整台设备的控制手段:其他标签页、其他浏览器和其他应用,各自都在安排自己的工作。

值得做的下一步:列出你加过哪些排除项、为什么加;确认客户端只挡隧道范围内的流量还是挡全部流量;并在打开服务器日志的情况下,把一条后台请求从头到尾看一遍。这三个答案,覆盖了「VPN 断了,但还有东西连着」这类报告里的大部分内容。

相关文章