先记住这个答案
HEAD 方法与 GET 语义等价,服务器应返回与 GET 相同的头部,但不包含响应体。它安全、幂等且可缓存,适合在发起大流量 GET 前探测资源是否存在、大小、类型或修改时间。典型用途包括检查链接有效性、获取文件大小、更新缓存验证等,能显著减少带宽消耗。客户端应忽略 HEAD 响应中意外的消息体。
- HEAD 返回头,不返回体,语义等同 GET
- 适合探测大文件大小、资源是否存在
- 用于缓存新鲜度检查和链接验证
HEAD 的请求处理与响应构造
浏览器或客户端发送 HEAD /path,服务器收到后运行与 GET 相同的路由和处理逻辑,生成完整响应对象(包括状态码、头部和潜在主体),但在传输前剥离主体,只发送状态行和头部。这也要求服务器在生成 Content-Length 时,应填入 GET 响应主体的长度,而不是 0,否则客户端无法预知实际大小。
响应中的头部字段(如 Content-Type、Last-Modified、ETag)应准确反映 GET 请求会得到的真实值。若服务器因实现错误在 HEAD 响应中发送了消息体,客户端必须忽略该体,并按响应头处理元数据,这些错误头部可能误导缓存和后续请求。
检查大文件大小后决定是否下载
假设一个资源下载链接可能指向一个 2GB 的镜像文件,用户客户端在下载前希望快速获知文件大小。使用 curl -I 发送 HEAD 请求,服务器返回 Content-Length: 2147483648 及 Content-Type: application/octet-stream,客户端据此判断文件过大,提示用户并放弃下载。
这样的探测仅产生一个很小的响应,不会占用下载带宽,也不需在磁盘上创建临时文件。若改用 GET,则会产生巨大的传输开销,且用户可能在中途取消,浪费服务器与网络资源。注意 HEAD 响应也可能被缓存,之后可用相同 HEAD 再次验证。
HEAD 失效与常见错误场景
一个典型失效场景是服务器处理 HEAD 时动态生成资源且未正确设置 Content-Length——有些框架会调用完整 GET 逻辑,却因缓存或压缩中间层导致头部不一致;更危险的是部分服务器或 CGI 脚本未处理 HEAD,将其转成 GET 并返回了主体。代理和客户端必须健壮地忽略主体,但若代理不加区分地透传,则可能让客户端误读元数据。
另一个边界是 HEAD 请求可能被不正确地缓存。RFC 要求 HEAD 响应可缓存,但缓存有效期必须与 GET 一致,且缓存条目能用于后续 GET 的条件验证。若服务器未设置正确的缓存头(如 Cache-Control),或缓存实现错误地使用 HEAD 响应来服务 GET,客户端可能拿到缺失主体或错误元数据。因此需要明确测试客户端的容错和缓存行为。
容易答错的地方
- 认为 HEAD 响应没有 `Content-Length`
- 正相反,HEAD 常需返回
Content-Length以告知 GET 的资源大小;没有它,客户端无法利用 HEAD 做下载判断。许多新手以为 HEAD 因为无主体就无长度,这是误解。 - 误以为 HEAD 和 GET 响应完全相同
- 头部应一致,但 HEAD 响应不应包含主体,因此如 Transfer-Encoding 等表示主体编码的头部可能不会出现,而 Content-Length 仍表示 GET 响应主体的大小。
面试官还会怎么问?
HEAD 支持重定向吗?
支持。服务器可以返回 3xx 重定向,客户端通常会自动跟随以获取最终资源的元数据,但也可配置不自动跟随;使用 curl -I -L 可查看最终元数据。
HEAD 可以与条件请求字段配合吗?
可以。带 If-None-Match 或 If-Modified-Since 的 HEAD 可用于验证缓存,即使没有 GET。若未修改返回 304,头部指示可用缓存,这比 GET 更省流量。
为什么 HEAD 请求有时会返回消息体?
多数源于服务器代码错误或中间层处理不当,比如框架没有显式处理 HEAD 而走 GET,输出了主体。客户端应忽略此体,但可能影响性能,最好修复服务端。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。