《图解HTTP》--第6章-HTTP首部

读《图解HTTP》:HTTP 首部到底在传递什么信息?

本文是阅读《图解HTTP》第 6 章后的学习整理,结合个人理解做了少量补充。文中的表格用于归纳本章概念,不替代原书内容。


为什么要单独看 HTTP 首部

前面几篇已经讲过,请求和响应是 HTTP 通信的基本形式。但如果只看"请求行、状态行、主体",我们其实还看不懂一次 HTTP 通信的完整意图。

比如:

  • 浏览器能接收什么格式的内容?
  • 服务器返回的资源能不能缓存?
  • 客户端请求的是完整资源,还是某一段字节范围?
  • 服务器要求客户端使用哪种认证方式?
  • Cookie 是谁下发的,又是谁带回来的?

这些信息大多不是放在主体里,而是放在 HTTP 首部字段里。

可以把 HTTP 报文简单理解成这样:

text 复制代码
起始行
首部字段
空行
主体

首部字段就像报文身上的说明标签:它不一定是用户最终看到的内容,却决定了客户端和服务器如何理解、传输、缓存、认证和处理这份内容。


首部字段长什么样

HTTP 首部字段由字段名和字段值组成,中间用冒号分隔:

http 复制代码
首部字段名: 字段值

例如:

http 复制代码
Content-Type: text/html

这里 Content-Type 是字段名,text/html 是字段值。

一个首部字段也可以有多个值:

http 复制代码
Keep-Alive: timeout=15, max=100

书中还特别提到一个容易被忽略的问题:如果同一个报文里出现多个相同字段名,不同浏览器或实现的处理结果可能不一致。有的会优先处理第一次出现的字段,有的会优先处理最后一次出现的字段。

所以在实际开发中,应该尽量避免生成重复含义的同名首部。


HTTP 首部分成哪几类

书中把 HTTP/1.1 的首部字段分成四类:

类型 使用位置 作用
通用首部字段 请求和响应都会使用 描述通信整体行为,比如缓存、连接管理、传输编码
请求首部字段 请求报文使用 补充客户端信息、请求条件、可接受内容等
响应首部字段 响应报文使用 补充服务器信息、认证要求、重定向位置等
实体首部字段 请求和响应的实体部分使用 描述实体主体本身,比如类型、长度、编码、修改时间

这四类不要死记,可以按问题来理解:

  • 通用首部:这次通信整体怎么处理?
  • 请求首部:客户端想要什么、能接受什么、带了什么条件?
  • 响应首部:服务器怎么回应、要求什么、资源在哪里?
  • 实体首部:报文主体是什么、怎么编码、有多大、什么时候改过?

书中列出了 HTTP/1.1 规范定义的 47 种首部字段。发布文章不适合逐字照搬成一大段,但我们可以先用一张总表建立全局印象:

类别 首部字段
通用首部 Cache-ControlConnectionDatePragmaTrailerTransfer-EncodingUpgradeViaWarning
请求首部 AcceptAccept-CharsetAccept-EncodingAccept-LanguageAuthorizationExpectFromHostIf-MatchIf-Modified-SinceIf-None-MatchIf-RangeIf-Unmodified-SinceMax-ForwardsProxy-AuthorizationRangeRefererTEUser-Agent
响应首部 Accept-RangesAgeETagLocationProxy-AuthenticateRetry-AfterServerVaryWWW-Authenticate
实体首部 AllowContent-EncodingContent-LanguageContent-LengthContent-LocationContent-MD5Content-RangeContent-TypeExpiresLast-Modified

另外,HTTP 通信中也会使用一些不属于 HTTP/1.1 RFC2616 定义、但非常常见的首部,比如 CookieSet-CookieContent-Disposition 等。书中也把 Cookie 相关首部单独拿出来讲,因为它在 Web 状态管理中太常用了。


端到端首部和逐跳首部

书中还提到一个很重要的分类:端到端首部和逐跳首部。

类型 含义 典型理解
端到端首部 会转发给最终接收目标,并且必须保存在缓存生成的响应中 信息要一路传到最终目的地
逐跳首部 只对单次转发有效,不会继续转发给下一站 信息只在当前这一跳生效

HTTP/1.1 中典型的逐跳首部包括:

  • Connection
  • Keep-Alive
  • Proxy-Authenticate
  • Proxy-Authorization
  • Trailer
  • TE
  • Transfer-Encoding
  • Upgrade

这个概念和代理转发场景很容易连起来理解:请求经过代理时,不是所有首部都应该原样传到最后。有些首部只服务于当前连接,到了下一跳就应该重新处理。


通用首部:控制通信整体行为

通用首部既可以出现在请求中,也可以出现在响应中。它们描述的是这次 HTTP 通信本身的处理方式。

最值得先记住的是这几个:

首部字段 作用 典型场景
Cache-Control 控制缓存行为 浏览器缓存、代理缓存、敏感数据不缓存
Connection 管理连接和逐跳首部 控制连接关闭、声明不再转发的首部
Date 表示报文创建时间 日志、缓存判断
Pragma HTTP/1.0 遗留字段 兼容旧缓存控制
Trailer 说明报文主体之后还会出现哪些首部 分块传输编码
Transfer-Encoding 指定传输报文主体时采用的编码方式 分块传输
Upgrade 切换到其他协议 协议升级
Via 记录代理或网关经过路径 排查代理链路
Warning 告知与缓存相关的警告 缓存过期、再验证失败等

其中 Cache-Control 是最容易在实际开发里碰到的。比如:

http 复制代码
Cache-Control: max-age=3600
Cache-Control: no-cache
Cache-Control: no-store

这里最容易误解的是 no-cache。它并不是"完全不缓存",而是"可以缓存,但使用前必须向源服务器确认有效性"。真正表示不保存请求和响应内容的是 no-store

Cache-Control 指令不能只记 no-cache

第 6 章里 Cache-Control 占了很大篇幅,因为它确实是缓存控制的核心。

常见请求指令可以这样理解:

请求指令 含义
no-cache 不直接接收缓存过的响应,要求缓存服务器转发请求并确认有效性
no-store 不保存请求或响应的任何内容
max-age 只接收 Age 不超过指定秒数的缓存响应
max-stale 即使缓存过期,也愿意接收一段时间内的响应
min-fresh 希望响应在指定时间内仍然保持新鲜
only-if-cached 只从缓存获取响应,不向源服务器确认

常见响应指令可以这样理解:

响应指令 含义
public 响应可以被任意缓存保存
private 响应只面向特定用户,通常不应被共享缓存返回给其他用户
no-cache 缓存使用前必须向源服务器确认有效性
no-store 不保存请求或响应的任何内容
max-age 响应在指定秒数内可视为新鲜
s-maxage 针对公共缓存服务器的最大缓存时间
must-revalidate 缓存过期后必须向源服务器再验证
proxy-revalidate 要求共享缓存服务器再验证
no-transform 缓存不能改变实体主体的媒体类型或内容编码

这也是第 5 章讲缓存时留下的问题:缓存服务器并不是"有缓存就直接返回",它需要根据这些缓存指令判断能不能用、要不要重新验证。

Connection、Upgrade、Via 的关系

Connection 有两个作用:

  • 控制不再转发给代理的首部字段
  • 管理持久连接

比如使用协议升级时,通常会同时出现:

http 复制代码
Upgrade: TLS/1.0
Connection: Upgrade

这表示 Upgrade 只对当前连接这一跳生效,不应该继续被代理无脑转发。

Via 则用于追踪报文经过的代理或网关。书中强调,经过代理时必须附加 Via,它既能追踪转发路径,也能避免请求回环。

Warning 主要用于告知用户一些与缓存相关的问题,比如响应已经过期、再验证失败、代理对内容做了转换等。


请求首部:客户端告诉服务器"我想要什么"

请求首部用于补充客户端的请求信息。它告诉服务器:客户端能处理什么内容、请求来自哪里、想访问哪个主机、是否带了认证信息、是否只想获取资源的一部分。

常见请求首部可以这样分组:

关注点 首部字段 作用
内容协商 Accept 告诉服务器客户端能处理哪些媒体类型
内容协商 Accept-Charset 告诉服务器客户端支持哪些字符集
内容协商 Accept-Encoding 告诉服务器客户端支持哪些内容编码
内容协商 Accept-Language 告诉服务器客户端偏好的自然语言
目标主机 Host 指定请求目标主机名和端口
认证 Authorization 携带客户端认证信息
期待行为 Expect 告诉服务器客户端期待某种特定行为,例如 100-continue
用户联系信息 From 告知服务器用户的电子邮件地址
条件请求 If-MatchIf-None-Match 根据 ETag 判断是否处理请求
条件请求 If-Modified-SinceIf-Unmodified-Since 根据资源修改时间判断是否处理请求
范围请求 RangeIf-Range 请求资源的某一部分
代理相关 Max-ForwardsProxy-Authorization 限制转发次数,或向代理提供认证信息
传输编码 TE 告诉服务器客户端能处理哪些传输编码
客户端信息 User-AgentReferer 表示客户端程序和请求来源

Host 为什么重要

Host 是 HTTP/1.1 中唯一必须包含的请求首部字段。

这和第 5 章的虚拟主机直接相关:多个域名可能解析到同一个 IP 地址,服务器必须通过 Host 判断客户端到底想访问哪个站点。

http 复制代码
GET / HTTP/1.1
Host: www.example.com

如果没有 Host,虚拟主机就很难正确区分不同站点。

Accept 系列在做什么

AcceptAccept-EncodingAccept-Language 这类字段,本质上是在做内容协商。

客户端不是简单地说"给我资源",而是在说:

  • 我能接受 HTML,也能接受 JSON
  • 我能处理 gzip 压缩
  • 我更偏好中文内容

服务器再根据这些信息,尽量返回最合适的资源表示。

条件请求是一组字段,不是一个字段

第 6 章里很多 If- 开头的字段都属于附带条件的请求。

字段 条件含义
If-Match ETag 匹配时才处理请求
If-None-Match ETag 不匹配时才处理请求
If-Modified-Since 指定时间后资源更新过,才处理请求
If-Unmodified-Since 指定时间后资源未更新,才处理请求
If-Range ETag 或时间匹配时按范围请求处理,否则返回完整资源

这些字段和缓存验证、断点续传、并发修改控制都有关系。

Max-Forwards 用来排查代理链路

Max-Forwards 会指定请求最多还能经过多少台服务器。每经过一次转发,数值减 1;当变成 0 时,接收请求的服务器直接返回响应。

它常和 TRACEOPTIONS 方法一起用于排查代理转发问题:如果请求在中间某一跳失败,可以通过控制 Max-Forwards 逐步定位问题发生在哪一段。


响应首部:服务器告诉客户端"我怎么回应"

响应首部用于补充服务器响应的信息。它可能告诉客户端资源位置、服务器信息、认证方式、范围请求能力,也可能影响缓存行为。

首部字段 作用 典型场景
Accept-Ranges 告知是否支持范围请求 下载续传、媒体资源
Age 告知源服务器在多久前创建了响应 缓存响应
ETag 表示资源实体标识 缓存验证、并发控制
Location 提供重定向目标 URI 3xx 重定向
Proxy-Authenticate 告知客户端代理服务器要求的认证信息 代理认证
Retry-After 告知客户端多久后再次请求 503 或重定向响应
Server 告知服务器软件信息 调试、排查
Vary 指定缓存需要参考哪些请求首部 内容协商缓存
WWW-Authenticate 告知客户端认证方案和质询信息 401 响应

ETag 和 Last-Modified 怎么配合缓存

严格说,ETag 是响应首部,Last-Modified 是实体首部,但它们经常一起出现在缓存验证场景里。

服务器第一次返回资源时,可以带上:

http 复制代码
ETag: "abc123"
Last-Modified: Wed, 23 May 2012 09:59:55 GMT

之后客户端再请求同一资源时,就可以通过条件请求询问服务器:

http 复制代码
If-None-Match: "abc123"
If-Modified-Since: Wed, 23 May 2012 09:59:55 GMT

如果资源没有变化,服务器就可以避免重复传输完整资源。

这里可以这样理解:

字段 判断依据 特点
ETag 资源实体标识 粒度更细,能表示资源版本
Last-Modified 最后修改时间 容易理解,但受时间精度影响

Vary 会影响缓存命中

Vary 用于告诉缓存服务器:缓存响应时,不能只看 URL,还要看某些请求首部字段。

例如:

http 复制代码
Vary: Accept-Language

这表示即使请求的是同一个资源,如果 Accept-Language 不同,也不能简单返回同一份缓存。否则英文用户可能拿到中文页面,或者反过来。

Retry-After 告诉客户端什么时候再来

Retry-After 常配合 503 Service Unavailable 或 3xx 重定向使用。字段值可以是具体日期时间,也可以是几秒之后。

它传达的是一种节奏控制:服务器现在无法处理,或希望客户端稍后再发起请求。


实体首部:描述主体本身

实体首部关注的是报文主体本身,而不是通信链路。

常见字段包括:

首部字段 作用
Allow 通知客户端指定资源支持哪些 HTTP 方法
Content-Type 说明实体主体的媒体类型
Content-Encoding 说明实体主体采用的内容编码
Content-Language 说明实体主体使用的自然语言
Content-Length 说明实体主体大小
Content-Location 给出与报文主体对应的 URI
Content-MD5 提供实体主体的报文摘要,用于检查完整性
Content-Range 范围请求响应中,说明返回的是实体的哪一部分
Expires 指定资源失效时间
Last-Modified 指定资源最后修改时间

比如一个 HTML 响应可能包含:

http 复制代码
Content-Type: text/html; charset=UTF-8
Content-Encoding: gzip
Content-Length: 12345

这三个字段分别说明:主体是什么类型、有没有压缩、主体有多大。

Allow 常出现在 405 Method Not Allowed 响应中,用来告诉客户端当前资源支持哪些方法:

http 复制代码
Allow: GET, HEAD

Content-Range 则和范围请求配套使用。服务器返回 206 Partial Content 时,会用它说明这次返回的是资源的哪一段:

http 复制代码
Content-Range: bytes 5001-10000/10000

Content-MD5 的目的在于让客户端检查报文主体是否完整。不过如果报文主体和 Content-MD5 字段本身都被篡改,客户端也无法仅凭它发现问题,这也是为什么传输安全不能只依赖单个字段。


HTTP 本身是无状态协议。为了让服务器识别"这次请求和上次请求来自同一个用户",Cookie 就派上了用场。

Cookie 相关首部主要有两个:

首部字段 方向 作用
Set-Cookie 服务器 -> 客户端 服务器要求客户端保存 Cookie
Cookie 客户端 -> 服务器 客户端在后续请求中带回 Cookie

典型流程是:

text 复制代码
服务器响应:Set-Cookie: session_id=abc123
客户端后续请求:Cookie: session_id=abc123

书中提到的 Set-Cookie 属性包括:

  • expires
  • path
  • domain
  • Secure
  • HttpOnly

其中 Secure 表示 Cookie 仅在 HTTPS 连接中发送,HttpOnly 可以防止 JavaScript 通过 document.cookie 读取 Cookie。涉及登录态的 Cookie,尤其要关注这些安全属性。

Cookie 的几个属性含义也值得单独记一下:

属性 含义
NAME=VALUE Cookie 的名称和值,必需项
expires Cookie 有效期;不指定时通常到浏览器关闭前为止
path 限制 Cookie 适用的服务器路径
domain 限制 Cookie 适用的域名范围
Secure 只在 HTTPS 安全通信时发送 Cookie
HttpOnly 限制 JavaScript 读取 Cookie,降低 XSS 窃取风险

书中还提到,服务器端没有一个"显式删除客户端 Cookie"的直接方法,常见做法是通过覆盖一个已过期的 Cookie,让客户端实现实质删除。


其他常见首部:安全和隐私相关

第 6 章最后还介绍了几个常见的非标准或扩展首部。

首部字段 类型 作用
X-Frame-Options 响应首部 控制页面是否允许被 frame/iframe 嵌入,用于防点击劫持
X-XSS-Protection 响应首部 控制浏览器 XSS 防护机制
DNT 请求首部 Do Not Track,表示用户拒绝被追踪的偏好
P3P 响应首部 与 Web 隐私策略相关

其中 X-Frame-Options 常见取值包括:

  • DENY
  • SAMEORIGIN

X-XSS-ProtectionP3P 都带有明显的时代背景;现代安全策略中,还需要结合 CSP、SameSite Cookie 等机制一起看。这里的重点是理解:HTTP 首部也常被浏览器和服务器用来表达安全、隐私和兼容性策略。


几组最容易混的首部概念

易混点 区别
no-cache vs no-store no-cache 可以缓存,但使用前要验证;no-store 是不保存请求或响应内容。
ETag vs Last-Modified ETag 按资源标识判断;Last-Modified 按修改时间判断。
Content-Encoding vs Transfer-Encoding 前者描述实体内容如何编码;后者描述传输过程中如何编码。
Authorization vs WWW-Authenticate 前者是客户端提交认证信息;后者是服务器发起认证质询。
Cookie vs Set-Cookie 前者是客户端带回 Cookie;后者是服务器下发 Cookie。
Host vs Server Host 表示客户端要访问谁;Server 表示服务器软件信息。
端到端首部 vs 逐跳首部 端到端首部传到最终目标;逐跳首部只对当前一次转发生效。

实践:用 curl 观察几个典型首部

下面是结合开发场景的补充实践,不是原书中的命令示例。

1. 查看响应首部

bash 复制代码
curl -I https://www.example.com/

可以重点观察:

  • Content-Type
  • Cache-Control
  • ETag
  • Last-Modified
  • Server

2. 使用 Accept 系列首部做内容协商

bash 复制代码
curl -H "Accept: application/json" https://api.example.com/data
curl -H "Accept-Language: zh-CN,zh;q=0.9" https://www.example.com/
curl -H "Accept-Encoding: gzip, deflate" --compressed https://www.example.com/

3. 使用条件请求验证缓存

bash 复制代码
curl -H 'If-Modified-Since: Wed, 23 May 2012 09:59:55 GMT' -I https://www.example.com/
curl -H 'If-None-Match: "abc123"' -I https://www.example.com/
bash 复制代码
curl -H "Cookie: session_id=abc123" https://www.example.com/dashboard
curl -u username:password https://www.example.com/admin/

小结

HTTP 首部不是可有可无的附加信息,它决定了 HTTP 通信中很多关键行为:

  • 内容协商靠 Accept 系列
  • 虚拟主机靠 Host
  • 缓存控制靠 Cache-ControlETagLast-Modified
  • 范围请求靠 Range
  • 认证交互靠 AuthorizationWWW-Authenticate
  • 状态管理靠 CookieSet-Cookie
  • 代理链路可以通过 Via 观察

如果说 HTTP 报文主体是"要传的内容",那么首部字段就是"如何传、如何理解、如何处理"的说明书。读懂首部,很多缓存、认证、压缩、重定向、代理相关的问题都会变得清晰。

不过,首部字段只能帮助双方传递控制信息,并不能解决 HTTP 的先天安全问题:明文会被窃听,身份可能被伪装,内容也可能被篡改。下一篇就顺着这个问题继续往下看:HTTPS 是怎样给 HTTP 补上安全能力的

相关推荐
BINGCHN1 小时前
CVE-2026-49975(HTTP/2 Bomb 远程拒绝服务漏洞)
网络·网络协议·http·cve
卓豪终端管理2 小时前
数据镜像:堵住U盘背后的数据漏洞
网络
代码熬夜敲Q2 小时前
网络工程相关
linux·服务器·网络
无风听海2 小时前
深入解析 ASP.NET Core 中的 Request.Cookies:从 HTTP 协议到加密存储与执行时序
后端·http·asp.net
呉師傅2 小时前
EPSON爱普生 L3118打印头【喷头】清洗方法
运维·服务器·网络·学习·电脑
酿情师3 小时前
区块链网络与跨链操作03:区块链的分叉
网络·centos·区块链
TheRouter3 小时前
LLM 推理成本工程:从 Token 计量到分层路由的生产降本实践
网络·ai
小鸡毛程序员3 小时前
从零搭建 Linux 开发服务器:VMware NAT 静态网络 + Docker + MySQL + Redis + 云服务器迁移
linux·服务器·网络
xlq223223 小时前
64.TCP 可靠性与效率
网络·网络协议·tcp/ip