FlowingDev

HAR 文件详解:你浏览器的黑匣子记录仪

学习 HAR (HTTP Archive) 文件是什么,它们如何捕获你浏览器的每一个网络请求,以及为什么它们对调试 Web 性能至关重要。

试用工具: HAR查看器

一言以蔽之

HAR 文件就是一个 JSON 格式的日志,记录了浏览器与网站交互的全过程,它以极其详尽的方式捕获了每一个网络请求和响应,以供后续分析。

它解决的问题

想象一下这个场景:一个远在天边的用户私信你,“你的应用慢到没法用。” 你自己试了试,快得很。他们说应用坏了,你说“在我这儿好好的”。僵局。

这就是 Web 开发中典型的“公说公有理,婆说婆有理”的状况。在现代浏览器工具出现之前,调试远程网络问题简直是一场噩梦,充满了猜测、翻查服务器日志,还得求着非技术用户描述那些天书般的错误信息。即便后来有了浏览器开发者工具(DevTools)和它那牛逼的 Network 标签页,问题依然存在:数据是短暂的。你没法轻易地把它打包发给同事。一张瀑布图的截图也说明不了全部问题。

于是,HTTP Archive 格式(简称 HAR)应运而生。它由 W3C 的 Web 性能工作组构想出来,旨在成为一个标准的、可共享的格式,用于……嗯,归档 HTTP 事务。它就相当于在用户的浏览器里装了个数字版的黑匣子记录仪。

HAR 文件通过捕获浏览器和服务器在加载特定网页时的全部网络对话,解决了“在我这儿好好的”这个问题。它记录了对图片、脚本、字体或 API 调用的每一个请求。它记录了发送的确切 header、交换的 cookie、遵循的 redirect,以及最重要的,请求每个阶段的精确时间。

这让一个在旧金山的开发者能够毫秒不差地看到一个在新加坡的用户到底经历了什么,而无需猜测。它使得那些转瞬即逝、难以复现的网络 bug 变得可以分析,并将“太慢了”这种模糊的抱怨转化为了可操作的数据。

底层工作原理

究其核心,HAR 文件没什么魔法。它就是一个巨大的、结构化的 JSON 文件。你可以在文本编辑器里打开它,看到所有内容,不过用专门的查看器来解析要容易得多。咱们来一探究竟吧。

宏观结构:它就是个 JSON

一个 HAR 文件包含一个顶级的 JSON 对象,这个对象只有一个键:log。其他所有东西都存在这个 log 对象里。

{
  "log": {
    "version": "1.2",
    "creator": { "name": "Chrome", "version": "118.0.0.0" },
    "browser": { "name": "Chrome", "version": "118.0.0.0" },
    "pages": [ /* ... one or more page objects ... */ ],
    "entries": [ /* ... one or more request/response objects ... */ ]
  }
}
  • version:HAR 规范版本,通常是 "1.2"。
  • creator / browser:关于生成此文件的工具和浏览器的元数据。有助于了解上下文。
  • pages:一个数组,描述了加载的一个或多个主页面。它包含页面标题和高阶事件(如 onLoad 和 onContentLoad)的时间点。
  • entries:这才是重头戏。它是一个长长的数组,其中每个对象都代表一个网络请求及其对应的响应。

重头戏:entries 数组

分析 HAR 文件时,你 99% 的时间都会花在 entries 上。每个 entry 都是关于一个资源的完整档案。

单个 entry 的简化版如下:

{
  "startedDateTime": "2023-10-27T10:30:05.123Z",
  "time": 258.45,
  "request": { /* ... details of the request ... */ },
  "response": { /* ... details of the response ... */ },
  "timings": { /* ... the juicy performance breakdown ... */ },
  "pageref": "page_1"
}
  • startedDateTime:请求开始时的精确 UTC 时间戳。
  • time:请求从开始到结束的总耗时,单位为毫秒。
  • request:一个包含浏览器发送给服务器所有内容的对象。
  • response:一个包含服务器返回的所有内容的对象。
  • timings:性能调试的金矿。我们接下来会详细剖析这个。
  • pageref:一个 ID,用于将此请求与 pages 数组中的某个 page 关联起来。

Request 和 Response 的剖析

request 和 response 对象就是你在 DevTools 中所看到内容的镜像。

request 对象详解:

  • method:GET, POST, PUT, 等。
  • url:资源的完整 URL。
  • headers:所有请求 header 的数组,如 User-Agent, Accept, 和 Cookie。
  • queryString:URL 上所有查询参数的数组。
  • postData:对于 POST 请求,这里存放着 payload,比如表单数据或 JSON body。

response 对象详解:

  • status:HTTP 状态码(例如 200, 404, 500)。
  • statusText:原因短语(例如 OK, Not Found)。
  • headers:所有响应 header 的数组,如 Content-Type, Cache-Control, 和 Set-Cookie。
  • content:描述响应体的对象,包括其 size、mimeType,以及通常在 text 属性中的响应体本身(不过为了节省空间或出于安全考虑,这部分可能会被省略)。

Timings 瀑布图分解

timings 对象是 HAR 查看器中五彩斑斓的瀑布图(waterfall chart)的动力来源。它将请求的总 time 分解为各个组成阶段。理解这些阶段是诊断请求“为什么”慢的关键。

Timing 它代表什么
blocked 请求在可以开始之前,在浏览器队列中等待的时间。通常是由于连接数限制。
dns 用于 DNS 查询的时间。如果这个值很高,可能意味着 DNS 提供商很慢。
connect 与服务器建立 TCP 连接所花费的时间。包括 ssl 时间。
ssl (connect 的一部分)用于 SSL/TLS 握手的时间。如果这个值很高,可能指向服务器配置或网络问题。
send 向服务器发送 HTTP 请求所花费的时间。通常很短。
wait 首字节时间 (TTFB)。 这是最关键的一个。指等待服务器处理请求并发送响应的第一个字节所花费的时间。wait 时间长几乎总是后端的问题。
receive 从服务器下载响应体所花费的时间。一个小文件 receive 时间长可能表示网络慢;而对于一个大文件,这很正常。

一个 entry 的总 time 是这些单个(非负)时间的总和。当 HAR 查看器为你显示一个请求的条形图时,它就是在视觉上将这些 timings 值首尾相接地堆叠起来。

真实世界案例

神秘的缓慢事件

一个抓狂的产品经理给团队发消息:“新的结账页面对我们最大的客户来说慢得要死!他们威胁要走人!” 开发团队试了试结账流程,快如闪电。客户坚持说确认订单需要 20 秒。开发负责人没有进行徒劳的来回拉扯,而是指导客户导出了一个 HAR 文件。

打开文件后,问题立刻就清楚了。在 entries 中,对 /api/v1/finalize_order 的 POST 请求总 time 为 20,145ms。查看 timings 对象,wait (TTFB) 超过了 20,000ms。后端服务器花了 20 秒才响应。原来,这个特定的客户有大量的订单历史,一个未优化的数据库查询超时了,但只对他们的账户如此。HAR 文件提供了确凿的证据,直接指向了某个特定的后端进程。

经验教训: HAR 文件能捕获你无法复现的、用户特定的情况(如账户数据),将一个谜团变成一个目标明确的 bug 报告。

臃肿代码包的罪魁祸首

一个营销网站上线后,跳出率高得吓人。感觉就是很重。一名前端开发者打开网站,打开 DevTools,录制了一个会话,然后导出了 HAR 文件。

在 HAR 查看器中,他们按大小对 entries 进行排序。排在最上面的是 main.acb123.js,足足有 5.2 MB。瀑布图显示它是一个渲染阻塞资源;在这个庞然大物下载完成之前,页面上什么都不会显示。单单 receive 时间就好几秒,即便是在快速连接上。更糟的是,查看这个 entry 的 response header,他们发现服务器没有发送 Content-Encoding: gzip header,尽管浏览器在 request header 中发送了 Accept-Encoding: gzip。这个 JavaScript 包根本没被压缩。

经验教训: HAR 文件能让你轻而易举地发现那些扼杀页面加载时间的性能杀手,比如超大资源和服务器配置错误。

无限重定向循环

一个用户抱怨说他们登不上。他们输入凭据,点击“登录”,然后立刻被踢回登录页,没有任何错误提示。这是一个经典的死循环。客服让用户提供一次登录尝试的 HAR 文件。

HAR 中的 entries 列表清晰地讲述了故事:

  1. POST /login 成功,并得到一个 302 Redirect 到 /dashboard。响应包含一个带有 session token 的 Set-Cookie header。
  2. 浏览器跟随重定向,发起一个 GET /dashboard 请求。
  3. 服务器对 GET /dashboard 的响应是一个 302 Redirect,又指回了 /login。

为什么?开发者检查了 GET /dashboard 请求的 entry。Cookie header 里没有 session token。然后他们检查了最初 POST /login 的响应。Set-Cookie header 是 session_id=...; Secure; HttpOnly。Secure 标志意味着浏览器只会通过 HTTPS 发送这个 cookie。而用户当时在 http://staging.example.com 这个环境上。浏览器正确地拒绝了通过不安全的连接发送安全 cookie,所以服务器就永远看不到他们已登录的状态。

经验教训: HAR 文件为你提供了 HTTP redirect 和 header 交换的完美逐帧回放,使得调试那些悄无声息失败的复杂认证流程成为可能。

常见错误和陷阱

  • 忘了勾选“Preserve log”。如果你的 bug 涉及从页面 A 跳转到页面 B,你必须在 DevTools 中启用“Preserve log”(或类似)选项。否则,日志会在导航时被清除,你的 HAR 文件将只包含页面 B 的请求。
  • 共享敏感数据。HAR 文件是无差别记录器。它们会捕获 API key、cookie 中的 session token,以及 POST body 中的个人身份信息。在公共的 bug 跟踪系统或论坛上共享 HAR 文件之前,一定要先做脱敏处理。
  • 误解 blocked 时间。高的 blocked 时间不一定意味着网络拥堵。浏览器对单个域名打开的并行连接数有限制(通常是 6 个)。如果你一次性发出 20 个图片请求,其中 14 个就会处于 blocked 状态,等待前 6 个中的某一个完成。
  • 忽略缓存状态。如果你在测试首次加载性能,你需要禁用浏览器缓存再进行录制。否则,你会看到大量的 304 Not Modified 响应或在 1 毫秒内完成的请求,这并不能反映新用户的体验。

为什么你应该关注它

只要网络通信有可能是问题根源,你就应该考虑使用 HAR 文件。

  • 当用户报告一个你无法复现的性能问题时。
  • 当你需要优化一个加载缓慢的页面并希望识别最大的瓶颈时。
  • 当你调试一个多步骤的 API 流程(如 OAuth 登录或支付过程)并需要查看确切的事件顺序时。
  • 当你需要向第三方服务(如 CDN 或 API 提供商)提交 bug 报告,并想为他们提供无可辩驳的问题证据时。HAR 文件是网络问题的通用语言。

深入了解

理论搞定,动手试试吧——100% 在你的浏览器中运行。

试用工具: HAR查看器