约 7 分钟读完 · 更新于 2026-10-21 · 文中每条都能在下面的配套工具里动手验证
状态码的第一位就是分类。排障时先看首位判断「该查谁」,再用具体数字定位环节——这一条口诀能省掉大半的来回猜测。客户端收到 4xx 先检查自己的请求参数和凭证,收到 5xx 才去找服务端日志。
| 状态码 | 什么时候用 | 最常踩的坑 |
|---|---|---|
| 200 OK | GET 成功返回 | 错误也返回 200(见下文反模式) |
| 201 Created | POST 建资源成功 | 忘了带 Location 指向新资源 |
| 204 No Content | 成功但没有返回体 | DELETE/PUT 后还想返回数据——那该用 200 |
| 301 / 302 | 永久 / 临时重定向 | 把临时跳转写成 301,会被浏览器和搜索引擎长期缓存 |
| 304 Not Modified | 协商缓存命中 | 响应里又带了 body,白省 |
| 400 Bad Request | 请求语法本身坏掉 | 把业务校验失败也塞进 400(该用 422) |
| 401 / 403 | 没登录 / 没权限 | 两者混用,客户端分不清该重新登录还是该换账号 |
| 404 / 410 | 资源不存在 / 已永久删除 | 接口下线返回 404 而不是 410,爬虫会一直重试 |
| 429 Too Many Requests | 触发限流 | 忘了带 Retry-After,客户端只能瞎重试 |
| 500 / 502 / 503 / 504 | 服务端异常 / 网关错误 / 暂不可用 / 网关超时 | 把可恢复的 503 当致命的 500 处理,错过重试时机 |
401 的语义是「我不知道你是谁」——没带凭证、凭证过期或无效,响应应带 WWW-Authenticate 头提示如何认证。403 的语义是「我知道你是谁,但你不能进」——身份合法但权限不足,重试没有意义。判断方法:换一个有权限的账号能解决的是 403,重新登录能解决的是 401。混用的代价是客户端不知道该跳登录页还是该提示无权限。
400 留给「请求本身就坏」:JSON 括号不配对、必填头缺失、编码错误——服务器读都读不懂。422 留给「读得懂但业务上不成立」:年龄传了负数、邮箱格式对但未注册、库存不足。这条界线在 REST 语义里很清楚,但实践中大量框架默认全报 400——如果团队约定统一用 400,就把错误明细放进响应体,别让客户端只能猜。
记法:3 开头里「七、八」保方法,「一、二」不保。给 API 做迁移,用 308 或 307;给页面做 SEO 迁移,用 301。302 的历史包袱最重——HTTP/1.0 时代几乎所有客户端都把它的 POST 降级成 GET,307 正是为了修正这个行为才补进标准。
| 状态码 | 永久性 | 请求方法 | 典型场景 |
|---|---|---|---|
| 301 | 永久 | 可能从 POST 降级为 GET | 域名迁移、URL 结构调整(搜索引擎转移权重) |
| 302 | 临时 | 可能从 POST 降级为 GET | 临时活动页、登录后的跳转 |
| 307 | 临时 | 原样保留(POST 仍是 POST) | 临时跳转且不能丢请求体(如表单提交) |
| 308 | 永久 | 原样保留 | 永久迁移且必须保留方法(如 API 端点换地址) |
客户端带 If-None-Match(配 ETag)或 If-Modified-Since(配 Last-Modified)发请求,资源没变时服务器只回 304 和几个头,不回 body——浏览器转而使用本地缓存。对静态资源和变更不频繁的 API 列表,这是零成本的提速手段。注意两点:304 响应别带 body;ETag 变了哪怕内容只差一个字节,也会老老实实回 200 全量。
有些 API 图省事:无论成败一律 200,把业务成败塞进响应体的 code 字段。代价在基础设施层:监控和告警按状态码统计,全 200 的服务故障时流量图一片绿;中间件的重试、熔断、限流也都依赖状态码判断,全 200 让这些机制集体失明。状态码表达传输层结果、body 表达业务结果,两层各司其职,系统才可观测。
HTTP/1.1 200 OK
{"code": 5001, "msg": "database unavailable"}
→ 网关看到 200,不熔断、不重试、不告警HTTP/1.1 503 Service Unavailable
Retry-After: 30
{"error": {"code": "DB_UNAVAILABLE", "message": "...", "trace_id": "..."}}401 是「没验明身份」:没带凭证、凭证过期,重新登录就能解决;403 是「验明了但权限不够」,换再多次登录也没用。给客户端的提示也不同:401 跳登录页,403 提示无权限。可以用 HTTP 状态码速查把整组 4xx 的语义一次看全。
502 意味着网关拿到了「非法响应」或连不上上游。常见原因:上游进程被 OOM 杀掉后被守护拉起(日志里表现为偶发中断)、响应体超网关 buffer 上限、上游主动断开 keep-alive 连接的竞态。按「网关错误日志 → 上游进程内存 → 响应大小」的顺序查。
你用的是 301/302,多数浏览器和客户端会把 POST 降级成 GET。想让方法原样保留,改用 307(临时)或 308(永久)。这是 HTTP 标准行为,不是 bug——降级语义写进了 301/302 的规范。
HTTP 中间层(网关、负载均衡、监控、重试与熔断组件)只认状态码。全 200 会让故障在流量图上不可见、让重试机制失效、让熔断器永远不打开。状态码表达传输层结果、body 表达业务结果,两层各司其职才可观测。