理解请求头:每个 HTTP 请求的自我介绍
每次你在浏览器输入网址,点击链接,或者刷新页面,你的浏览器都在向服务器发送一段自我介绍。
这段介绍不是可选的。它是 HTTP 协议的一部分。服务器需要这些信息来理解你的请求,决定如何响应。
这些信息就是请求头。
请求头是一种元数据
请求头不是你要获取的内容本身。它们是关于请求的信息。
GET /api/test HTTP/1.1
Host: localhost:3001
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)
Accept: text/html,application/xhtml+xml
这个请求在说:我想要 /api/test 的内容。我运行在 macOS 上的 Chrome 浏览器。我能理解 HTML 和 XHTML 格式。
💡 Click the maximize icon to view in fullscreen
服务器根据这些信息做决定。它可能返回压缩的内容,选择正确的语言版本,或者验证你的身份。
为什么请求头是必需的
没有请求头,服务器就是盲目的。它不知道你是谁,不知道你能接受什么格式,不知道你从哪里来。
{
host: 'localhost:3001',
connection: 'keep-alive',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9',
'accept-encoding': 'gzip, deflate, br, zstd',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8'
}
host 告诉服务器你要访问哪个域名。同一个 IP 可能运行多个网站。
connection: keep-alive 说明你希望复用 TCP 连接。不要每个请求都建立新连接。
accept-encoding 列出你支持的压缩格式。服务器可以发送压缩的响应,节省带宽。
accept-language 表明你偏好的语言。服务器可以返回对应的翻译版本。
这些不是额外的负担。它们让通信变得高效。
安全相关的请求头
现代浏览器发送很多以 sec- 开头的请求头。
{
'sec-ch-ua': '"Google Chrome";v="135", "Not-A.Brand";v="8"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
'sec-fetch-site': 'none',
'sec-fetch-mode': 'navigate',
'sec-fetch-user': '?1',
'sec-fetch-dest': 'document'
}
这些是浏览器自动添加的,JavaScript 无法修改。它们用于防止跨站攻击。
sec-fetch-site: none 表示这是直接导航,不是从其他网站跳转过来的。
sec-fetch-mode: navigate 说明这是页面导航,不是 AJAX 请求。
sec-fetch-user: ?1 表示这个请求是用户主动触发的,不是脚本发起的。
服务器可以用这些信息来判断请求是否合法。如果一个请求声称是用户导航,但 sec-fetch-user 是 ?0,那可能是恶意脚本。
sec-fetch-* 系列头部是 Fetch Metadata Request Headers 标准的一部分。
这些头部让服务器能够区分:
- 合法的用户请求 vs 恶意的跨站请求
- 页面导航 vs 资源加载
- 同源请求 vs 跨域请求
它们是现代 Web 安全防护的重要一环,帮助服务器实施更细粒度的访问控制策略。
缓存和条件请求
浏览器会缓存访问过的内容。请求头用来协商缓存。
{
'if-none-match': 'W/"76-rkc5H5n4Qg7J5LXH9vLJ1APwCCo"',
cookie: 'next_hmr_refresh_hash=005b63d0ef94285a74de84603926c45395ad660cee88b0fc'
}
if-none-match 是一个 ETag。它是资源的指纹。浏览器在说:我有一个版本,指纹是这个。如果内容没变,不用再发一遍。
服务器检查 ETag。如果匹配,返回 304 Not Modified,不发送内容。浏览器用缓存的版本。
这节省了带宽,加快了加载速度。
cookie 头部携带会话信息。服务器用它来识别你的身份,保持登录状态。
请求头透露的信息
请求头里包含很多关于你的信息。
user-agent 告诉服务器你的浏览器、操作系统、设备型号。
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36
这个字符串说明:
- 操作系统是 macOS 10.15.7
- 浏览器是 Chrome 135
- 渲染引擎是 WebKit/Blink
网站用这些信息来适配不同的浏览器。但它也可以用来追踪你。
加上 accept-language、sec-ch-ua-platform、屏幕分辨率、时区等信息,可以构成一个相当独特的指纹。
浏览器指纹(Browser Fingerprinting)是一种追踪技术,通过收集浏览器特征来识别用户:
- User-Agent 字符串
- 屏幕分辨率和色深
- 时区和语言设置
- 安装的字体列表
- Canvas 指纹
- WebGL 指纹
- 音频指纹
即使禁用 Cookie,这些信息的组合也能在大多数情况下唯一识别一个用户。
隐私保护工具(如 Brave、Firefox 的增强隐私模式)通过标准化这些特征来对抗指纹识别。
隐私与功能的权衡
请求头是功能性的。它们让 Web 正常工作。
但它们也暴露信息。你无法在享受个性化服务的同时完全隐藏自己。
这是协议层面的权衡。HTTP 设计时,隐私不是首要考虑。功能性和兼容性才是。
现代浏览器尝试减少不必要的信息暴露。Chrome 逐步淘汰 User-Agent,用 User-Agent Client Hints 替代。这些新头部只在需要时发送详细信息。
但基础的请求头仍然是必需的。Host、Accept、Connection 这些无法删除。没有它们,HTTP 就无法工作。
服务器如何使用请求头
从服务器的角度,请求头是决策的依据。
app.get('/api/test', (req, res) => {
console.log('请求头:', req.headers)
// 根据 Accept 返回不同格式
if (req.headers.accept.includes('application/json')) {
res.json({ message: 'JSON 响应' })
} else {
res.send('<html>HTML 响应</html>')
}
})
根据 accept-language 返回对应语言的内容。根据 user-agent 判断是移动设备还是桌面设备,返回不同的样式。根据 cookie 识别用户身份,显示个性化内容。
请求头让服务器能够智能地响应。它们是客户端和服务器之间的约定。
💡 Click the maximize icon to view in fullscreen
最后
请求头是 HTTP 通信的基础设施。它们让服务器理解你的需求,让浏览器和服务器协商出最佳的交互方式。
每个请求头都有它的用途。有些关于功能,有些关于安全,有些关于性能。
它们不是可有可无的元数据。它们定义了 Web 如何工作。
当你看到一个 HTTP 请求的完整日志,你看到的不只是技术细节,而是浏览器和服务器之间的对话。每个头部都在说一些事情。理解这些对话,就理解了 Web 的运作方式。
Related Posts
Articles you might also find interesting