HTTP 状态码详解

从 200 到 504,彻底搞懂每个状态码的含义

HTTP 状态码是客户端与服务端沟通的语言。每次请求返回的三位数字,包含了请求是否成功、出了什么问题、下一步该怎么做等重要信息。然而很多开发者只认识 200、404、500,遇到其他状态码时一头雾水。本文系统梳理了 HTTP 状态码的分类与常见问题排查方法。

1. 状态码分类总览

HTTP 状态码按第一位数字分为五大类,每一类代表不同的含义:

2. 2xx 成功:细说常见成功状态

200 OK           // 请求成功,最常见的状态码
201 Created      // 请求成功且创建了新资源(POST/PUT)
204 No Content   // 成功但无返回体(DELETE 常用)
206 Partial Content // 部分内容(断点续传/视频流)

206 Partial Content 的应用

206 是 HTTP 分片传输的核心。客户端通过 Range 请求头指定字节范围,服务端返回 206 状态码和对应范围的数据。这在视频播放、大文件下载、断点续传中非常重要。

# 请求头
Range: bytes=0-1023

# 响应头
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1023/10000
Content-Length: 1024

3. 3xx 重定向:容易混淆的几种跳转

重定向状态码看似相似,实则在语义和行为上有重要区别,特别是对 SEO 有直接影响。

301 Moved Permanently   // 永久重定向(SEO 传递权重)
302 Found               // 临时重定向(原始,可能改变请求方法)
303 See Other           // 临时重定向,强制 GET 方法
304 Not Modified        // 资源未修改(协商缓存)
307 Temporary Redirect  // 临时重定向,保持请求方法
308 Permanent Redirect  // 永久重定向,保持请求方法

301 vs 302:SEO 的关键区别

301 是永久重定向,搜索引擎会把原 URL 的权重转移到新地址;302 是临时重定向,搜索引擎会保留原 URL 的索引。如果 URL 结构永久变更(如网站改版、域名更换),务必使用 301。

304 与缓存机制

304 是浏览器缓存优化的核心。当浏览器请求已缓存的资源时,会带上 If-None-MatchIf-Modified-Since 请求头。如果服务端判断资源未变化,就返回 304,浏览器直接使用本地缓存,节省了传输时间和带宽。

# 请求头(浏览器)
If-None-Match: "abc123"
If-Modified-Since: Wed, 21 Oct 2024 07:28:00 GMT

# 响应头(服务器)
HTTP/1.1 304 Not Modified
ETag: "abc123"

4. 4xx 客户端错误:你的请求有问题

4xx 错误表示客户端的请求有问题,服务端无法处理。排查时应先检查请求参数、格式、权限等。

400 Bad Request       // 请求参数错误/格式不对
401 Unauthorized      // 未认证/未登录
403 Forbidden         // 已认证但无权限访问
404 Not Found         // 资源不存在
405 Method Not Allowed // 请求方法不允许(如 GET 接口用了 POST)
408 Request Timeout   // 请求超时
409 Conflict          // 资源冲突(如重复创建)
410 Gone              // 资源已永久删除(比404更强)
413 Payload Too Large // 请求体过大
415 Unsupported Media Type // 不支持的媒体类型
422 Unprocessable Entity   // 语义错误(参数格式对但内容错)
429 Too Many Requests // 请求过于频繁(限流)

401 vs 403:别再搞混了

401 Unauthorized 的意思其实是"未认证"——你没有登录,或者 token 过期了。403 Forbidden 才是"无权限"——你登录了,但这个资源你不能看。简单记:401 = 你是谁?403 = 我知道你是谁,但你不行。

429 与限流

429 是 API 限流的标准状态码。服务端应同时返回 Retry-After 响应头,告诉客户端多久后可以重试。遇到 429 时,客户端应实现退避重试(exponential backoff),而不是立即疯狂重试。

5. 5xx 服务端错误:服务器出问题了

5xx 错误表示服务端在处理请求时发生了错误。遇到 5xx 时,责任通常在服务端,但也可能是客户端的请求触发了服务端的未处理边界。

500 Internal Server Error  // 服务器内部错误(通用)
501 Not Implemented        // 服务器不支持该功能
502 Bad Gateway            // 网关错误(反向代理收到无效响应)
503 Service Unavailable    // 服务不可用(过载/维护)
504 Gateway Timeout        // 网关超时(上游服务响应太慢)
507 Insufficient Storage   // 存储空间不足

502 vs 504:网关相关的两个常见错误

这两个错误都和反向代理(Nginx、负载均衡器)有关。502 Bad Gateway 表示代理从上游服务器收到了无效响应(比如上游服务挂了、返回了无法解析的内容)。504 Gateway Timeout 表示代理等了太久都没收到上游响应,超时了。简单说:502 = 上游返回了垃圾,504 = 上游响应太慢。

6. 常见问题排查思路

遇到异常状态码时,按以下思路排查可以少走很多弯路:

  1. 先看网络面板:打开浏览器 DevTools 的 Network 面板,查看完整的请求和响应头
  2. 确认 URL 和方法:404/405 首先检查 URL 是否正确、HTTP 方法是否匹配
  3. 检查请求体格式:400/415 通常是 Content-Type 不对或 JSON 格式错误
  4. 认证信息:401 检查 Authorization 头,403 检查用户角色权限
  5. 查看服务端日志:500/502 必须查服务端日志才能定位根因
  6. 用 curl 复现:把请求复制为 curl 命令,排除浏览器缓存和扩展的干扰

推荐两个调试工具:curl 命令在线工具可以快速构造和测试 HTTP 请求;Postman 或 Thunder Client 则适合更复杂的 API 调试场景。

7. 不常见但有用的状态码

除了常见的 200、404、500,还有一些状态码虽然不常用,但在特定场景下非常实用。了解它们可以让你的 API 设计更专业。

100 Continue          // 客户端可以继续发送请求体
101 Switching Protocols // 协议切换(如 WebSocket 升级)
202 Accepted          // 请求已接受,正在处理(异步任务)
203 Non-Authoritative Information // 代理返回的修改过的响应
300 Multiple Choices  // 多个选择,需要用户选择
402 Payment Required  // 预留状态码,将来可能用于付费
406 Not Acceptable    // 无法生成客户端可接受的内容
407 Proxy Auth Required // 需要代理认证
410 Gone              // 资源永久删除(比404语义更强)
416 Range Not Satisfiable // 请求的范围无效
418 I'm a teapot      // 愚人节玩笑,部分框架真的实现了
421 Misdirected Request // 请求被发送到了无法产生响应的服务器
425 Too Early         // 可能存在重放攻击风险
426 Upgrade Required  // 客户端需要升级协议
451 Unavailable For Legal Reasons // 因法律原因不可用
506 Variant Also Negotiates // 内部配置错误
508 Loop Detected     // 检测到循环
510 Not Extended      // 需要进一步扩展

其中 202 Accepted 特别适合异步任务场景——客户端提交任务后,服务端立即返回 202 和任务 ID,客户端通过轮询或 WebSocket 来获取任务执行结果。451 则是一个很有意思的状态码,专门用于表示因法律原因(如版权、审查)导致内容不可用,命名来源于小说《华氏 451》。

8. API 设计中状态码的选择原则

设计 API 时,正确选择状态码很重要。一个常见的错误是所有错误都返回 200,然后在响应体里放自定义错误码。这种做法虽然"灵活",但违背了 HTTP 语义,会导致缓存、监控、代理等基础设施无法正常工作。

正确的做法是:HTTP 状态码表示协议层面的结果,响应体中的业务错误码表示具体的业务错误。两者配合使用,既符合 HTTP 语义,又能提供足够详细的错误信息。

选择状态码时的判断逻辑:

  1. 请求成功了吗?→ 2xx
  2. 需要跳转吗?→ 3xx
  3. 是客户端的问题吗?(参数错、没登录、没权限、资源不存在)→ 4xx
  4. 是服务端的问题吗?(代码 bug、服务挂了、超时)→ 5xx

拿不准时,选语义最接近的那个。比如参数格式错误用 400,参数内容不合法用 422,资源不存在用 404,操作冲突用 409。语义准确的状态码,能让前端和运维少走很多弯路。

最后补充一个小知识:状态码的每一位数字都有特定含义。第一位表示类别(成功/重定向/客户端错误/服务端错误),第二位表示更细的分类(如 40x 是语法/认证类错误,41x 是内容协商/长度类错误,42x 是语义/限流类错误)。理解这些规律,遇到不认识的状态码也能猜个八九不离十。