HTTP 状态代码 何时返回 404、410 或 301(以及停止猜测)
后端开发者经常错误地使用 HTTP 状态码。这里是一份实际有用的 REST API 中重要状态码的指南——404 与 410、301 与 302、429 速率限制,以及 200 响应体中包含错误信息的反模式。
您返回了200状态码,伴随 {"error": "user not found"} 在响应体中。您的301状态码实际上应为302。已删除的资源会永远返回404。我们来梳理开发者最常出错的HTTP状态码,以及应该返回的正确状态码。
4xx 与 5xx:并非同一类
HTTP状态码中最基本的划分: 4xx是客户端的问题,5xx是服务器的问题。理论上简单,实践中却屡屡被违反。
最常见的错误:返回 500状态码用于验证错误。用户向您的API发送了格式错误的JSON。这是 400 请求错误 ——他们的请求体有误。返回500意味着“服务器崩溃”,这会触发警报,被记录为服务器错误,并导致自动化系统重复尝试(使问题更加严重)。如果客户端发送了错误数据,请使用4xx状态码明确指出。
404 与 410:已删除资源的问题
404 未找到 表示:“我现在没有这个资源,但稍后再试一下。”爬虫(包括Googlebot)将404视为临时状态。它们会无限次重新检查。
410 已移除 表示:“该资源已被永久删除,请不要再请求了。”Googlebot会比404更快地从索引中移除410链接,并且会更早停止爬取这些页面。这对SEO和大型网站的爬取预算至关重要。
实际规则:如果您的API彻底删除了一条记录且不会再恢复,请在后续请求中返回410。只有在资源可能被恢复的情况下(如软删除、有恢复窗口的发布文章等),才考虑使用404。否则,410更诚实,搜索引擎也会因此感谢您。
301 与 302(以及307/308存在的原因)
301 永久移动:该URL已永久移除,请使用新地址。浏览器会强烈缓存301响应。搜索引擎会将链接权重传递给目标地址。如果您正在合并域名或永久迁移网站,301是您需要的。
302 找到:暂时跳转到另一个URL,但稍后仍需返回原地址。不缓存。不传递链接权重。适用于登录重定向、临时维护页面或A/B测试。
错误模式:将302用于永久移动。您认为“本质上相同,只是地址不同”,但搜索引擎不会通过302传递链接权重。多年来的SEO工作仍附着在旧URL上,而新地址则无法获得排名。如果进行了永久移动,请使用301。
此外还有 307 临时重定向 且 308 永久重定向。这些是方法安全的变体:当浏览器在跟随301/302时可能将POST降级为GET,而307/308则保留原始HTTP方法。如果您正在重定向接受POST请求体的API端点,307(临时)或308(永久)可以保持方法不变。
| 代码 | 类型 | 缓存? | 保留方法? | 适用于 |
|---|---|---|---|---|
| 301 | 永久 | 是的 | 不(可能降级为GET) | 永久网站迁移、URL合并 |
| 302 | 临时 | 不 | 不(可能降级为GET) | 登录重定向、临时不可用 |
| 307 | 临时 | 不 | 是的 | API POST端点的临时重定向 |
| 308 | 永久 | 是的 | 是的 | API POST端点的永久重定向 |
401 与 403:认证与授权的区别
命名令人困惑。 401 未授权 实际上表示 未认证 ——您未登录,或凭据缺失或无效。 403 禁止 表示您已认证,但无权访问该资源。
实际区别:401应提示登录界面或刷新令牌。403应显示“访问被拒绝”的消息。返回错误的状态码会使客户端无法判断是需要请求凭据还是放弃请求。
此外还有安全层面。某些API在调用者无法访问资源时返回404而非403,以避免泄露资源存在。如果您在另一个租户的资源上返回403,就确认了该资源存在。返回404更安全,尽管这会使合法用户调试更困难。根据您的威胁模型选择合适的权衡并保持一致。
429:带有效头的速率限制
仅返回 429 请求过多 令人恼火。带有 Retry-After Retry-After
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1746374400
Retry-After 头的429才是真正有用。该头可以是秒数或HTTP日期。良好行为的客户端会自动退避。没有该头,您只是返回错误码并希望客户端自行判断何时重试——大多数客户端会立即再次请求,这完全违背了速率限制的目的。
不要为速率限制返回503。503表示“服务不可用”——基础设施故障、部署中进行、断路器打开。速率限制是策略决策,而非服务故障。429正是为此而存在的。
“200伴随错误体”反模式
这种模式在生产API中频繁出现,必须停止:
HTTP/1.1 200 OK
Content-Type: application/json
{"status": "error", "message": "user not found"}
这破坏了所有依赖HTTP语义的功能。监控工具将请求标记为成功。负载均衡器会将其传递。客户端库不会抛出异常。日志看起来干净,而用户却看到错误。解决方法只需一行代码:返回正确的状态码。 200 表示操作成功。 如果操作失败,请不要返回200。
同样的逻辑适用于资源创建中的201与200。如果POST到 /users 创建了新用户,请返回 201 已创建 SOAP在XML之上增加了一层:每个响应都被包裹在一个 Location 头,指向新资源。如果返回200,则会丢失客户端可用来避免额外往返请求的信息。
API场景 → 正确状态码
以下是REST API开发中最常见的场景参考表:
| 设想 | 状态码 | 笔记 |
|---|---|---|
| 请求体为格式错误的JSON | 400 请求错误 | 包含指向解析错误的提示信息 |
| 请求中缺少或无效字段 | 422 不可处理的实体 | 422比400更精确,适用于语义验证失败 |
| 认证令牌缺失或已过期 | 401 未授权 | 包含 WWW-Authenticate Authorization |
| 已认证但无访问权限 | 403 禁止 | 或在需要隐藏资源存在时返回404 |
| 资源未找到,可能返回 | 404 未找到 | 用于临时缺失或未知资源 |
| 资源已被永久删除 | 410 已移除 | 爬虫会更早停止重新检查 |
| 创建了新资源 | 201 已创建 | 添加 Location: /resources/new-id Authorization |
| DELETE操作成功,无响应体 | 204 无内容 | 不要用200返回空的JSON对象 |
| 永久URL变更 | 301 永久移动 | 搜索引擎会将链接权重传递给目标地址 |
| 临时重定向 | 302 找到 | 不缓存,不传递链接权重 |
| 超出速率限制 | 429 请求过多 | 始终包含 Retry-After |
| 意外的服务器异常 | 500 内部服务器错误 | 不要在响应体中泄露堆栈跟踪 |
| 上游依赖服务不可用 | 503 服务不可用 | 添加 Retry-After 如果停机时间有界 |
| POST重定向,保留方法 | 307 或 308 | 307 临时,308 永久 |
当您在进行PR合并时需要确认状态码的实际含义,可以使用 HTTP状态码查找工具 获取其规范定义、典型用例以及对应的RFC文档。
检查重定向链
在网站迁移或URL重构后,值得验证完整的重定向链——特别是当301重定向后来被更新为指向其他地址时,会形成多跳链,导致链接权重流失。可以使用 重定向检查器 追踪整个链路并按顺序显示每个状态码,以确认所有请求都能在单跳内解析。
