状态码有 60 多个,但日常真会影响客户端行为的不到二十个。判断一个状态码值不值得记,就问一句:它会不会让浏览器、SDK 或网关改变行为?不会的(比如 418)知道名字就行。
| 类别 | 含义 | 谁的问题 |
|---|---|---|
| 1xx | 信息,继续 | 协议内部,很少手写 |
| 2xx | 成功 | — |
| 3xx | 需要进一步动作(重定向) | 客户端要重新发请求 |
| 4xx | 客户端错误 | 请求本身有问题,重试同样的请求没用 |
| 5xx | 服务端错误 | 服务端暂时处理不了,换个时间重试可能有用 |
| 码 | 名称 | 关键点 |
|---|---|---|
| 200 | OK | 有响应体 |
| 201 | Created | 资源已创建,应在 Location 头给出新资源 URL |
| 202 | Accepted | 已接收但还没处理完,异步任务的正确返回 |
| 204 | No Content | 成功但没有响应体,DELETE 常用 |
| 400 | Bad Request | 请求格式错,比如 JSON 解析失败 |
| 401 | Unauthorized | 未认证:缺少凭证或凭证无效,应带 WWW-Authenticate |
| 403 | Forbidden | 已认证但没权限,重试没用 |
| 404 | Not Found | 不存在;如果不想暴露资源是否存在,也可用 403 |
| 405 | Method Not Allowed | 方法不支持,必须在 Allow 头列出可用方法 |
| 409 | Conflict | 与当前资源状态冲突,比如唯一约束冲突 |
| 422 | Unprocessable Entity | 语法没问题但语义处理不了,常用于表单校验失败 |
| 429 | Too Many Requests | 限流,应带 Retry-After |
401 和 403 是最常搞混的一对。一句话区分:401 是"你是谁",403 是"你不配"。没登录或 token 过期返回 401;登录了但没权限访问这个资源返回 403。
400 和 422 的区别在于"请求能不能被解析":JSON 语法错误是 400,JSON 合法但 age 字段是 -5 是 422。
这是最容易写错的一组。核心变量有两个:是不是永久、以及重定向时能不能改请求方法。
| 码 | 永久? | 会不会改方法 | 用在哪 |
|---|---|---|---|
| 301 | 永久 | 历史上客户端常把 POST 改成 GET | 域名迁移、URL 结构变更 |
| 302 | 临时 | 历史上客户端常把 POST 改成 GET | 临时跳转 |
| 303 | 临时 | 明确要求改用 GET | POST 提交后跳到结果页(PRG 模式) |
| 307 | 临时 | 不允许改方法,原样重发 | 临时跳转但要保住 POST 体 |
| 308 | 永久 | 不允许改方法 | 永久跳转但要保住 POST 体 |
301 和 302 的"改方法"是历史实现的惯性行为,规范并没有这么要求,所以才补了 307 和 308 两个语义确定的版本。需要保住 POST 请求体时,用 307 或 308,不要用 301 或 302。
304 Not Modified 单独说:它不是重定向,而是告诉客户端"你缓存的版本还新鲜,直接用",响应体必须为空,配合 ETag 或 Last-Modified 使用。
| 码 | 含义 | 典型原因 |
|---|---|---|
| 500 | Internal Server Error | 未捕获异常,兜底码 |
| 502 | Bad Gateway | 网关/反向代理收到了上游的无效响应 |
| 503 | Service Unavailable | 服务暂时不可用(发版、过载),可带 Retry-After |
| 504 | Gateway Timeout | 网关等上游等到超时,上游可能只是慢 |
502 和 504 的差别是"上游回了话但话不对"对"上游压根没回话"。排查方向完全不同:502 去看上游是不是崩了或者返回了畸形响应;504 去看上游是不是慢查询卡住,以及网关的超时阈值是不是设短了。
| 情况 | 该不该重试 |
|---|---|
| 5xx(除 501) | 可以,但要退避(指数退避 + 抖动) |
| 429 / 503 | 可以,且必须遵守 Retry-After 指定的时间 |
| 408 / 504 | 请求未生效时可重试 |
| 4xx(400/401/403/404/422) | 不要重试,同样的请求再发一万次还是错 |
| POST 类的 5xx | 谨慎。POST 不幂等,重试可能产生重复订单 |
POST 如果需要安全重试,让客户端带 Idempotency-Key 头,服务端据此去重。
一、所有响应都返回 200。 把业务错误塞进 body 的 {"code": 50001} 里,HTTP 状态一律 200。后果很具体:CDN 不看 body,会把错误响应缓存起来;监控系统的错误率指标永远是 0;HTTP 客户端库的重试逻辑完全失效。
二、把业务校验失败返回 500。 参数不合法是客户端的问题,应该给 400 或 422。返回 500 会让监控告警把"用户输入错误"当成"线上故障"。
顺带两个彩蛋码:451 表示因法律原因不可用(RFC 7725),是正经状态码;418 I'm a teapot 来自 RFC 2324 的愚人节玩笑,不要用在真实业务里。