HTTP头部字段基础:请求头与响应头结构、常见字段分类、自定义字段规范
说起HTTP头部,我第一个想到的就是那句老话——“协议是明文,内容靠自己”。HTTP的头部字段,说白了就是客户端和服务器之间传递的“信封”。信封上写什么,决定了对方怎么理解你。不管是HTTPS水印嵌入还是安全扫描,底层都在和这些字段打交道。
我个人习惯把HTTP头部当成“元数据走廊”。你发一个请求,服务器能通过头部知道你的浏览器、语言偏好、是否缓存,甚至能通过自定义字段传递额外的令牌或水印参数。嗯,今天这节课,咱们就把这个“走廊”的结构、分类和自定义规范彻底理清楚。
请求头与响应头:结构差异
HTTP头部字段分两头:请求头(Request Headers) 和 响应头(Response Headers)。结构上其实一模一样——都是“键: 值”对,每行一个,用CRLF(回车换行)隔开。但用途完全不同。
| 头部类型 | 发送者 | 典型字段 | 我在项目中遇到过的坑 |
|---|---|---|---|
| 请求头 | 客户端(浏览器、APP、脚本) | Host, User-Agent, Accept, Cookie, Authorization | 有次客户端发请求时漏了Host头,服务器直接啃了半天没认出域名 |
| 响应头 | 服务器 | Content-Type, Set-Cookie, Cache-Control, Server, WWW-Authenticate | Content-Type写错成text/plain,浏览器不肯渲染HTML页面 |
| 通用头 (可在两端出现) |
双方 | Date, Connection, Via, Cache-Control | Cache-Control在两边都能设,优先级逻辑经常搞混 |
| 实体头 | 描述消息体 | Content-Length, Content-Encoding, Last-Modified | Content-Length算错,导致接收端卡在“等待更多数据”里 |
请求头的结构示例:
GET /api/watermark HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: application/json
Authorization: Bearer xxxxxx
响应头的结构示例:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Set-Cookie: sessionid=abc123; Secure; HttpOnly
Cache-Control: no-cache
X-Watermark-Version: 2
你想想看,头部顺序其实不重要,但字段名不区分大小写,这在我刚学的时候吃过亏——我写了个content-type,但服务器只认Content-Type?其实不,HTTP标准说大小写无所谓,但许多框架内部会规范化。我曾经在调试日志里看到大小写不一致,排查了半天才发现是两个团队用的库不一样。
常见字段分类:四大家族
我把头部字段画成四大家族,这样记起来更快:
- 通用头(General Headers)——发给谁都行,不关心具体资源。
典型:Date、Connection、Cache-Control、Via、Upgrade。 - 请求头(Request Headers)——只从客户端到服务器。
典型:Host、User-Agent、Accept、Referer、Authorization、Cookie。 - 响应头(Response Headers)——只从服务器到客户端。
典型:Server、Set-Cookie、WWW-Authenticate、Access-Control-Allow-Origin。 - 实体头(Entity Headers)——描述消息体的元信息。
典型:Content-Type、Content-Length、Content-Encoding、Last-Modified、ETag。
X-*自定义字段和Content-Type——水印参数经常被塞进实体头或自定义请求头里。我做过一个案例,水印字段写在X-Watermark-Payload里,但因为Content-Type没设对,代理直接把这个自定义头给吞了。嗯,疼过一回就记住了。
自定义字段规范:从X-到正式注册
自定义字段,就是你自己取名的头部字段。比如我习惯用X-Request-ID做链路追踪,用X-Watermark-传递水印参数。
早期的惯例是加X-前缀,表示非标准、实验性的字段。但RFC 6648(2012年)已经废弃了这个要求——因为X-前缀会导致标准化困难,而且内部使用的字段也可能被其他系统误判。现在的建议是什么?
- 不强制X-前缀,但建议内部使用:如果你只在内部系统或边缘节点传递水印,用
X-Watermark-Version完全没问题,只要能保证不冲突。 - 正式注册的字段不带X-:如果你想把字段推向标准化,需要遵循RFC 3864,字段名要描述行为而不是加前缀。例如
Watermark-Version、Watermark-Payload。 - 大小写与连字符:标准推荐首字母大写并用连字符(
X-Watermark-Expires),当然实际解析时大小写不敏感。我在项目里统一用小写+连字符(x-watermark-expires),团队内保持风格一致就好。
X-开头的头部字段,除非你显式配置白名单。如果你的水印字段依赖自定义头通过代理,一定要检查代理的策略,否则字段会消失得无声无息。
本章核心逻辑图谱
为了帮你一眼看清HTTP头部字段的整体结构,我画了一张分层架构图。它展示了请求头、响应头、通用头、实体头四大家族的位置关系,以及自定义字段的位置和约束。
这个图我画的是典型的四层结构:请求头在左侧,响应头在右侧,通用头和实体头在下方,自定义字段在这四类中都可以出现——但需要注意标准化和代理兼容性问题。
我在项目中常见的自定义字段陷阱
- 代理吞噬X-头——有些反代(如Nginx默认配置)会上报
X-Real-IP,但也会丢弃其他未知的X-*头。记住:要显式配置proxy_set_header。 - 多值拼接问题——如果同一个字段名出现多次,有的客户端取第一个值,有的取最后一个,有的取逗号拼接。我在一个项目中用
X-Watermark传递了两次值,结果服务器只收到了最后一次,差点以为水印丢了。 - 超长字段截断——HTTP头部总大小有限制(通常是8KB),如果水印字段里塞了太长的Base64编码,可能直接被截断。建议水印参数用压缩+短编码。
总结一句话
HTTP头部字段就是你的“信息容器”。请求头、响应头、通用头、实体头各司其职,自定义字段给了你最大灵活性,但也带来了兼容性风险。做HTTPS水印时,我建议先搞清楚你的代理链、服务框架和API网关的头部处理策略,再决定字段名字和值格式——否则水印字段被吞了,你都不知道去哪找。