HTTP 响应中包含什么?
每次浏览器加载页面或 JavaScript 调用 fetch() 时,服务器都会返回一个 HTTP 响应。你看到的是结果——渲染的页面、一些 JSON 数据或错误消息——但响应本身包含的结构比大多数开发者有意识地思考的要多。理解这种结构能让你在 DevTools 中调试或在代码中处理响应时拥有更清晰的思维模型。
一个 HTTP 响应包含三个部分:状态行、响应头和可选的响应体。
核心要点
- 每个 HTTP 响应由三部分组成:状态行(或 HTTP/2 和 HTTP/3 中的
:status伪头部)、响应头和可选的响应体。 - 状态码按其首位数字分组——2xx 表示成功,3xx 表示重定向,4xx 表示客户端错误,5xx 表示服务器错误。
- 响应头控制缓存、安全、Cookie 和内容解析——它们告诉浏览器如何处理载荷,而不仅仅是载荷是什么。
- 在跨域请求中,并非所有响应头都能被前端 JavaScript 访问。服务器必须使用
Access-Control-Expose-Headers来允许访问默认安全集合之外的响应头。
HTTP 响应的结构:状态、响应头、响应体
状态行
在 HTTP/1.1 中,响应以单行状态行开始:
HTTP/1.1 200 OK
这一行包含协议版本、三位数的状态码和人类可读的原因短语。原因短语仅供参考——浏览器和客户端实际上根据状态码采取行动。
HTTP 状态码按其首位数字分组:
| 范围 | 含义 |
|---|---|
| 1xx | 信息性响应(请求已接收,继续处理) |
| 2xx | 成功(200 OK、201 Created、204 No Content) |
| 3xx | 重定向(301 Moved Permanently、304 Not Modified) |
| 4xx | 客户端错误(400 Bad Request、401 Unauthorized、404 Not Found) |
| 5xx | 服务器错误(500 Internal Server Error、503 Service Unavailable) |
关于 HTTP/2 和 HTTP/3 的说明: 上述文本状态行是 HTTP/1.1 传输格式特有的。在 HTTP/2 和 HTTP/3 中,没有状态行。相反,状态通过 :status 伪头部传递(例如 :status: 200)。语义是相同的——状态码的含义相同——但表示形式不同。DevTools 会规范化表示形式,因此无论使用哪个协议版本,你都会看到熟悉的状态码。
HTTP 响应头:它们实际上做什么
响应头是元数据。它们告诉浏览器如何处理响应,而不是内容是什么。每个响应头都是一个不区分大小写的名称,后跟冒号和值。
以下是你最常遇到的类别:
内容元数据
Content-Type: application/json; charset=utf-8— 告诉浏览器响应体的格式以及如何解码。Content-Length: 1024— 响应体的字节大小。
缓存
Cache-Control: max-age=3600, public— 指示浏览器和 CDN 缓存响应的时长。ETag: "abc123"— 资源的指纹,用于条件请求。如果资源未更改,服务器返回304 Not Modified而不是完整的响应体。
安全
Content-Security-Policy— 限制浏览器可以从哪些来源加载脚本、样式和其他资源。Strict-Transport-Security— 告诉浏览器在指定时间内仅通过 HTTPS 连接。
Cookie
Set-Cookie: session=xyz; HttpOnly; Secure— 指示浏览器存储 Cookie。单个响应可以包含多个Set-Cookie响应头,每个 Cookie 一个。
一个重要的限制:并非所有响应头都能被前端 JavaScript 访问。默认情况下,Fetch API 仅从跨域响应中公开一小部分”安全”响应头。服务器必须在 Access-Control-Expose-Headers 中明确列出额外的响应头,你的 JavaScript 才能读取它们。
响应体
响应体是实际的载荷——HTML、JSON、图像、文件。并非每个响应都有响应体。204 No Content 或 304 Not Modified 响应会有意省略响应体。在这些情况下,状态码本身就是消息。
Content-Type 响应头告诉浏览器如何解释响应体中的内容。
Discover how at OpenReplay.com.
值得了解的不太常见的特性
HTTP 响应还可以包含信息性响应,如 103 Early Hints,它允许服务器在主响应到达之前建议预加载资源。尾部响应头(Trailers)——在响应体之后发送的响应头——存在于 HTTP/1.1(使用分块传输编码)以及 HTTP/2 和 HTTP/3 中作为尾部响应头,但在日常前端工作中很少遇到。这些值得了解,但你不会经常在 DevTools 中看到它们。
结论
将 HTTP 响应想象成一个信封。状态码告诉你交付是否成功。响应头是外面的说明——小心处理、开封后冷藏、24 小时后过期。响应体是里面的内容。当出现问题时,首先检查状态码,然后检查响应头——它们通常会准确告诉你出了什么问题以及接下来该做什么。
常见问题
Content-Length 指定响应体的确切字节大小。Transfer-Encoding 通常设置为 chunked,意味着响应体被分块发送,没有预定的总大小。在 HTTP/1.1 中,响应使用其中之一。分块编码常用于动态生成的内容,服务器事先不知道最终大小。
对于跨域请求,Fetch API 默认只公开有限的 CORS 安全列表响应头。这些包括 Cache-Control、Content-Language、Content-Type、Expires、Last-Modified 和 Pragma。要从 JavaScript 访问任何其他响应头,服务器必须在 Access-Control-Expose-Headers 响应头中包含它。
当服务器成功处理请求但没有响应体要返回时,使用 204 No Content。这在 DELETE 操作或表单提交中很常见,客户端不需要响应中的更新数据。当你想向客户端发送确认载荷或更新的资源时,200 OK 更合适。
使用 F12 或 Ctrl+Shift+I 打开 DevTools,转到 Network(网络)选项卡,然后点击列表中的任何请求。Headers(响应头)面板显示请求和响应头。你还可以按请求类型筛选并搜索特定的响应头名称。Response(响应)选项卡显示服务器返回的原始响应体内容。
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.