不喜欢广告? 无广告 今天

真正会伤害你的HTTP状态码——301与302、401与403,以及5xx错误陷阱

发布日期

不是字典。是一份专注于导致实际生产问题的HTTP状态码的指南——错误的重定向现在被永久缓存,401/403混淆泄露了你的认证逻辑,429错误缺少Retry-After头,以及503与504的混淆让你误入错误的调试层级。

这不是一个字典。这是一份专注于导致真实生产问题的 HTTP 状态码的指南——错误的重定向现在已被永久缓存,401/403 混淆泄露了您的认证逻辑,缺少 Retry-After 的 429,以及 503 与 504 混淆导致您在错误的层级上进行调试。
广告 移除?

您发送了一个重定向,但类型错误。现在所有在您之前访问过该重定向的浏览器都已永久缓存了它,唯一的解决方法是等待缓存过期,或者请求用户清除浏览器历史记录。这是 301 状态码。

这不是一个状态码字典——这类资料很多。这是在您阅读了表格后仍然发送了错误代码时需要的指南。我们正在讨论那些导致实际问题的状态码:您本想使用临时重定向却用了永久重定向,认证错误泄露了信息,未正确处理的速率限制响应,以及指向错误层级的网关超时。

301 与 302 与 307 与 308:重定向矩阵

每个人至少会犯一次的错误:您使用 301 Moved Permanently 在测试 URL 重构时。它有效。您继续进行。然后您再次重构。现在一部分用户——在第二次变更之前访问过您的用户——将被永久发送到旧的地址,且该地址已永久缓存在他们的浏览器中。

301 是永久性的,且会激进地缓存。 浏览器将重定向缓存的 TTL 设置为近乎无限,除非您设置了 Cache-Control 头信息。目前没有程序化方法可以从用户的浏览器中清除该缓存。如果您仍在确定 URL 结构,请使用 302。

方法保留问题是一个不同的问题。当浏览器跟随 301 或 302 重定向时,它 可能会 (实际上几乎总是会) 将 POST 降级为 GET。这在技术上违反了原始的 HTTP/1.0 规范,但浏览器早已将其标准化,RFC 7231 也承认这是标准行为。如果您正在重定向一个 POST 请求——例如表单提交之后——并且希望保留方法,您需要使用 307 或 308。

代码永久性?保留方法?用于
301是的否(POST → GET)永久 URL 移动,GET 在重定向后即可使用
302否(POST → GET)临时重定向,功能标志,A/B 测试
307是的临时重定向,且必须保留方法
308是的是的永久重定向,且必须保留方法

308 的支持现在非常稳定——Chrome、Firefox、Safari 和 Edge 都能正确处理它。但要注意:一些较旧的 API 客户端和 HTTP 库仍未能正确遵循 308,因此如果您正在重定向机器对机器的流量,并且无法控制客户端及其 HTTP 库,必须明确测试。

401 与 403:认证与授权

401 表示“您尚未进行身份验证”。 规范要求它必须包含一个 WWW-Authenticate 头信息,告知客户端如何进行认证。当没有有效会话、没有令牌,或令牌已过期时,这是正确的状态码。

403 表示“我知道你是谁,但答案是‘否’”。 用户已通过认证,但缺乏权限。在令牌过期时返回 403 是错误的——这应是 401。在有效但权限不足的请求中返回 401 也是错误的,更糟糕的是,这会混淆您的认证调试:您会寻找缺失的凭据,而真正的根本问题是角色分配。

在返回 404 而不是 403 的安全论点是真实的。如果您的 API 在 返回 403,您实际上就告诉了任何攻击者该端点存在,他们需要更高权限才能访问它。返回 404 则不会泄露资源存在的信息。这是一个判断:403 在技术上是正确的,且更容易调试;404 是对敏感端点中可枚举性至关重要的安全选择。 GET /admin/users429:缺少 Retry-After 头的速率限制是无用的

没有

头的 429 响应是一个黑箱。客户端知道已被速率限制,但不知道应该等待 100 毫秒还是 24 小时。大多数客户端实现要么立即重试(导致您的速率限制器被击穿),要么直接放弃。 Retry-After Retry-After 头可以是整数(等待秒数)或 HTTP 日期(重试时间):

Retry-After 头信息并未出现在任何 RFC 中,而是源自 GitHub API 的事实标准,被广泛复制。请在

HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1749254400

X-RateLimit-* 头信息中同时包含 Retry-After。Limit/Remaining/Reset 三者组合告诉客户端在达到限制前的状态,这比仅仅知道已触碰到限制更有用。

值得一提的一点是: Retry-After 在 503 响应中也是有效的,其含义相同——“请在若干秒后再次尝试”。如果您的服务因维护窗口暂时不可用,请发送一个带有 Retry-After 的 503 响应,而不是简单地断开连接。

503 与 504:失败发生在何处?

这两个看起来相似,但指向完全不同的故障层级,混淆它们会将您引入错误的调试方向。

503 服务不可用 表示您访问的服务器当前无法处理请求——它可能过载、处于维护模式,或后端依赖服务已宕机。服务器本身已响应,只是拒绝处理请求。

504 网关超时 表示代理或网关(如您的负载均衡器、Nginx、API 网关、CDN)尝试连接上游服务器但未在规定时间内收到响应。您访问的服务器是存活的,但其后端服务无法响应。

实际应用中:如果您在 Nginx 之后,而应用服务器(Node、Rails、Django 等)崩溃,您会收到 502 错误——Nginx 收到了响应,但内容是垃圾。如果应用服务器完全停止响应,您会收到 504。如果在应用层故意返回错误,您会收到 503。区分这一点在故障排查中至关重要:504 表示应检查上游服务、超时配置和网络。503 表示应检查应用本身。

422 与 400:验证错误有其专属代码

400 请求错误 用于服务器无法解析的请求——如格式错误的 JSON、查询字符串语法错误、缺少必需的头部。这是一个结构问题。

422 不可处理的实体 用于服务器理解了请求但因数据验证失败而无法处理的情况——如起始时间晚于结束时间、邮箱字段包含无效地址、数量字段为负值。请求在语法上是正确的,但语义上是错误的。

大多数 API 将两者都合并为 400,并将验证细节隐藏在响应体中。这虽然可行,但使客户端错误处理更困难:客户端必须解析响应体才能判断是解析错误还是验证错误。使用 422 作为验证错误代码,可以让客户端直接通过状态码判断。Rails 早已正确实现这一点;JSON:API 规范也规定 422 用于验证错误。

一个细微差别:422 定义于 WebDAV(RFC 4918),而非核心 HTTP 规范。在实践中这并不重要——所有 HTTP 客户端和服务器都能正确处理它——但您偶尔会遇到一个固执的人坚持应使用 400。他们并非完全错误。但 422 更具体且被更广泛理解。

204 与 200 带空体:DELETE 的正确响应

当 DELETE 成功时,正确的响应是 204 无内容 ——而不是 200 带空体,也不是 200 带 {"success": true}.

204 是明确表示“该操作成功且有意无响应体”的信号。200 带空体在技术上是有效的,但存在歧义——客户端不知道是本应存在但丢失了体,还是本意就是无体。204 消除了这种歧义。

同样的逻辑适用于 PUT 和 PATCH,当您不返回更新后的资源时。如果您的 API 在 PATCH 后返回更新后的对象,请使用 200;如果未返回,请使用 204。不要返回 200 带空体——那是 204 的伪装。 {}null 一个常见陷阱:

204 必须不包含消息体 根据 RFC 9110。如果您返回 204 时意外包含体(某些框架允许),一些 HTTP 客户端会优雅处理,而另一些则不会。请在响应处理器中移除体,而不仅仅是意图层面。 快速参考:容易引发问题的状态码及其原因

常见错误

代码意义修复方案永久重定向
301在测试期间使用——现在已被永久缓存在确认重定向为永久前使用 302POST 在跟随时被降级为 GET
302临时重定向如果必须保留方法,请使用 307临时重定向(方法安全)
307与 302 混淆在临时重定向 POST/PUT/PATCH 时使用永久重定向(方法安全)
308习惯性跳过 302 而选择 301当方法重要时,使用 307 而非 301请求解析错误
400也用于验证错误对于语义验证失败,使用 422未认证
401在用户缺乏权限时返回(应返回 403)仅在凭据缺失或已过期时返回 401在安全敏感端点中,用 404 替代 403
403禁忌当隐藏端点存在性至关重要时,考虑使用 404不可处理的实体
422被合并到 400用于验证失败,且请求体可解析的情况速率限制
429缺少 Retry-After 头始终包含 Retry-After 和 X-RateLimit-* 头服务不可用
503与 504 混淆当目标服务器无法处理请求时使用网关超时
504与 503 混淆应调查上游服务,而非网关无内容
204返回 200 带空体代替对于成功的 DELETE/PUT/PATCH 且无响应体,使用 204如果您需要在调试时快速查找任何状态码,

IO Tools 提供了一个 HTTP 状态码查找表 涵盖了完整范围,并附有描述和使用每个状态码的说明。 所有这些错误背后的模式

这些错误大多源于同一个地方:将状态码视为松散类别(“4xx 是客户端错误,5xx 是服务器错误,结束”)而非协议中具有特定语义的元素。语义很重要,因为客户端——浏览器、HTTP 库、CDN、监控工具——实际上会依据这些语义进行判断。CDN 不会以相同方式缓存 200 和 204。HTTP 客户端不会以相同逻辑重试 400 和 503。浏览器不会以相同 TTL 缓存 302 和 301。

使用正确的状态码。开销为零。当出现问题时,调试带来的收益是巨大的。

HTTP 状态码中真正会“咬人”的情况——301 与 302、401 与 403,以及 5xx 的陷阱 2

想要享受无广告的体验吗? 立即无广告

安装我们的扩展

将 IO 工具添加到您最喜欢的浏览器,以便即时访问和更快地搜索

添加 Chrome 扩展程序 添加 边缘延伸 添加 Firefox 扩展 添加 Opera 扩展

记分板已到达!

记分板 是一种有趣的跟踪您游戏的方式,所有数据都存储在您的浏览器中。更多功能即将推出!

广告 移除?
广告 移除?
广告 移除?

新闻角 包含技术亮点

参与其中

帮助我们继续提供有价值的免费工具

给我买杯咖啡
广告 移除?