HTTP状态码这东西,平时不显山不露水,可一旦线上出问题,大家第一眼盯的基本都是它。开发联调时接口突然报错,你问后端“返回啥了”,对方甩过来一个数字;运维查工单时,翻到网关访问日志,满屏都是三位数;前端接后端接口,只要状态码不是 2xx,页面就直接白屏。这时候,一个能快速对照的 HTTP 状态码清单,比什么都管用。
这份清单不是让你背的,是拿来查的。我把平时开发、排查、联调、运维这些场景里遇到的状态码,按分类整理成文,把每个状态码的含义、典型场景、常见原因、排查方向一次性讲清楚。适合后端开发、前端开发、测试、运维,以及刚学 HTTP 协议但不知道它到底有什么用的人。你不需要把几十个状态码全部记下来,只需要知道大类是什么、遇到具体码时知道去哪个方向查,效率就能提升一大截。
1. HTTP 状态码基础认知
1.1 状态码长什么样、藏在哪
打开浏览器的开发者工具,随便访问一个网页,在网络面板里点开任意一条请求,能看到响应头、响应体,最上面那一行就是状态行。例如访问一个不存在的页面会看到:
code复制HTTP/1.1 404 Not Found
这一行包含三部分:HTTP 版本、状态码、原因短语。状态码就是中间那个三位数字,机器根据它决定下一步处理;原因短语只是给人看的简短描述,没有任何强约束力。服务端返回 404 时,哪怕原因短语写成 "Oops",客户端依然会认为请求找不着资源。
所以,真正决定语义的是状态码本身。三位数字的第一位是类别标记,第二位和第三位是细分编号。比如 2xx 这一大类里,200 表示完全成功,201 表示创建成功,204 表示成功但没内容。数字范围是固定的,不能自定义 299、399 这种没有官方含义的码,这会破坏客户端对类别的判断。如果你在自研协议里用了非标准码,客户端拿到的响应状态根本无法归类,后续解析基本就是灾难。
1.2 五大分类到底在说什么
HTTP 状态码一共五大类,规则非常简单:第一位是几,就代表什么性质的结果。我平时习惯记成“服务器在跟你对话”:1xx 是“我知道了,还在处理”,2xx 是“成了”,3xx 是“你走错门了,我告诉你新地址”,4xx 是“是你发给我的东西有问题”,5xx 是“我自己出问题了”。
| 分类 | 范围 | 含义 | 常见情况 |
|---|---|---|---|
| 信息性响应 | 100-199 | 请求已接收,服务器继续处理 | 很少直接出现在业务代码里 |
| 成功 | 200-299 | 请求已成功处理 | 日常接口最常见的 200、201、204 |
| 重定向 | 300-399 | 需要进一步操作才能完成请求 | 301、302、304 最常见 |
| 客户端错误 | 400-499 | 请求本身有问题 | 参数错误、没权限、资源不存在 |
| 服务端错误 | 500-599 | 服务器处理时出错 | 后端代码崩了、网关超时 |
这个大分类一定要刻在脑子里,因为很多冷门状态码虽然没怎么见过,但只要你知道它是 4xx,第一反应就应该是“去查请求参数、Header、权限”,而不是去重启服务器。反过来,看到 5xx,才需要去查后端服务和中间件。这一条判断在线上排障时特别省事。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 高频状态码逐个拆解
2.1 开发中最常打交道的 12 个状态码
我不打算把所有状态码都扔给你,先把日常出现频率最高的十几个讲透。
- 200 OK:请求成功,响应体里就是你要的数据。GET 查询、POST 提交成功后一般就返回它。
- 201 Created:新建资源成功。POST 创建一条数据、PUT 上传一个文件成功后,规范做法是返回 201,同时带上新资源的 Location 头。
- 204 No Content:处理成功,但没有内容返回。比如删除一个资源成功后,返回 204 比返回 200 更准确,前端不需要解析响应体。
- 301 Moved Permanently:永久重定向。域名换了、旧链接要永久指向新地址时用这个。浏览器和搜索引擎都会记住新地址。
- 302 Found:临时重定向。比如用户未登录时跳转到登录页,登录完还能回到原页面,这时候用 302。
- 400 Bad Request:请求本身不对。要么是参数缺失、格式错误,要么是请求体不是服务端想要的,反正问题出在客户端。
- 401 Unauthorized:没有认证,或者认证信息无效。通常意味着“需要登录”或“登录已过期”。
- 403 Forbidden:服务器知道你是谁,但你没有权限访问。注意它和 401 的差别。
- 404 Not Found:资源不存在。可能是路径写错、资源被下线,也可能是服务端故意隐藏真实资源的存在。
- 500 Internal Server Error:服务端代码执行时抛异常了,没拦住。这个码是后端自己人最怕的。
- 502 Bad Gateway:网关或中间服务器收到了上游服务器的无效响应。Nginx 后面挂了服务,上游连不上或返回异常,Nginx 就会给出 502。
- 503 Service Unavailable:服务器暂时无法处理请求,通常是因为过载或正在维护。它不是代码 bug,更像是“现在忙不过来”。
- 504 Gateway Timeout:网关在等待上游响应时超时。上游服务处理得太慢,或者压根没起来。
这十二个状态码是面试、联调、排障的最高频选手。剩下的状态码你在工作中零星遇到,拿本文最后那张完整清单对照即可。
2.2 那些长得像、含义却差很远的状态码
分组对比能帮你少踩很多坑。
先说 301 和 302。区别在于“这次跳转是不是永久的”。301 是永久性的,浏览器和搜索引擎会把旧地址的权重转移到新地址;302 是临时的,浏览器每次都先去请求旧地址,拿到 302 后再跳转到新地址。如果拿 302 做永久跳转,很容易出现权重分散、短地址失效后页面无法找回的问题。我见过有同学因为图省事把整个站点域名跳转都配成 302,结果新域名收录一片惨淡。
再说 401 和 403。我习惯用这个比喻解释:401 是“你谁啊?先证明一下身份”,403 是“我知道你是谁,但你不配进这个门”。所以,未登录返回 401,前端拿到后应该去跳登录页;已登录但访问了管理员接口,返回 403,前端只需要提示“没有权限”。如果反过来用,前端逻辑会很混乱。
404 和 410 也是一对容易忽略的组合。404 表示“这个资源现在不存在”,但服务器没说它以前是否存在过;410 表示“这个资源以前有,现在永久删了,别再来了”。从 SEO 角度讲,410 比 404 更明确,搜索引擎知道该彻底移除这条记录而不是继续爬。
500、502、503、504 这四个 5xx 是最容易让人头疼的。500 是应用代码崩了;502 是网关和上游之间通信出了问题;503 是服务整体不可用;504 是网关等上游等超时。排查时顺序很重要:先看 5xx 出现在哪个层。Nginx 的日志里出现 502 或 504,大概率是后面应用服务的事;应用日志里出现 500,再往代码堆栈里追。
3. 从一条报错日志反推状态码排查流程
3.1 400 的真实现场:响应体会告诉你答案
很多新手看到 400 Bad Request 就懵了,觉得“我请求明明发了,怎么就 Bad 了”。其实 400 是最好查的一类错误,因为问题几乎都出在客户端请求本身,而且服务端通常会在响应体里把具体原因告诉你。
我之前接入一个第三方大模型接口时,遇到过一条报错:
code复制upstream_status: http 400
cause: thinking mode 下 reasoning_content 必须回传给接口
这个报错里 400 只说明请求被拒绝,真正有用的是 cause 那句话。它告诉我,请求里需要带上上一轮回复里的 reasoning_content 字段,而我漏传了。遇到这种情况,正确的做法是先打开响应体,看看 message、cause、error 这些字段具体写了什么,再回头检查请求参数、Header、Content-Type、JSON 格式,而不是反复重发同样的请求碰运气。
400 也可能来自请求体类型不对,例如服务端需要 application/json,你却传了 application/x-www-form-urlencoded,这时候大概率直接 400 或 415。还有一种是 URL 参数里带非法字符,比如未编码的中文或空格,服务端解析失败也会返回 400。排查时可以配合抓包或开发者工具,把实际发出的请求头和请求体完整看一遍。
3.2 502 和 504:网关与上游服务之间的链路问题
502 和 504 是最典型的“中间传话人”状态码。你请求一个接口,网关先把请求转给后面的应用服务,应用服务响应后网关再把结果返回给你。如果应用服务没响应、响应异常、或者响应太慢,你看到的最终状态码就是 502 或 504。
我记得有一次本地联调,日志里出现一行:
code复制unexpected status 502 bad gateway: unknown error, url: http://127.0.0.1:1572
我第一反应是“1572 端口的服务是不是没启动”。打开任务管理器一看,进程果然挂了。启动服务后再请求,状态码恢复正常。这种场景在本地特别常见:端口被占用、服务没起来、或者起了多个实例导致端口冲突,都会让中间层给出 502。
排障步骤基本可以固定成一套:
- 先确认请求从哪个入口进来,是直接打到应用服务,还是先经过网关。
- 如果是经过网关,去看网关日志里有没有记录上游地址和响应耗时。耗时接近超时阈值,大概率是 504;连不上或响应异常,大概率是 502。
- 去上游服务所在机器看进程是否存活,日志最后几行有没有报错。
- 有负载均衡的话,把所有节点逐个检查一遍,可能有单节点异常。
这里有个容易忽视的点:502 不一定都是服务端挂了,也可能是上游服务的响应格式不合法。比如上游返回了非 HTTP 协议的数据,网关解析不了,就会拒绝转发。遇到这种 "unknown error",建议直接拿 curl 请求上游地址,看看它到底返回什么内容。
3.3 状态码排查速查表
整理一张常用速查表,遇到问题直接按表查方向。
| 状态码 | 典型现象 | 优先排查方向 |
|---|---|---|
| 400 | 接口提示请求无效 | 查看响应体,检查参数、Content-Type、请求格式 |
| 401 | 需要登录或登录失效 | 检查 token、cookie、Authorization 头 |
| 403 | 无权限访问 | 检查账号角色、接口权限配置 |
| 404 | 资源不存在 | 检查路径、路由、资源是否被删除 |
| 405 | 接口返回方法不允许 | 检查请求方法是否匹配接口定义 |
| 429 | 请求频繁被限流 | 看限流策略,是否需要降速或扩容 |
| 500 | 后端代码报错 | 看应用日志堆栈,定位异常位置 |
| 502 | 网关收不到上游有效响应 | 检查上游进程、端口、服务状态 |
| 503 | 服务暂不可用 | 检查是否过载、维护中、注册中心是否摘除节点 |
| 504 | 网关等待上游超时 | 检查上游响应耗时、接口慢查询、连接池配置 |
这张表不是万能的,但能覆盖日常八成以上的状态码排查。
4. 接口设计里状态码该怎么用才不背锅
4.1 先定规则:状态码不是业务错误的垃圾桶
写 API 的时候,状态码设计得好不好,直接决定前后端联调要加多少班。最让人头疼的设计是“所有接口不管成功失败一律返回 200,然后把错误信息塞在响应体的某个字段里”。这种设计在部分老系统里很常见,但它的缺点是:HTTP 层面的语义没有了,监控系统没法基于状态码统计接口健康度,前端也需要解析完响应体才能判断成败,一旦忘记判断就是异常数据入库。
我更推荐的做法是让 HTTP 状态码表达“请求本身能不能处理”,再用响应体里的业务错误码表达“具体业务失败原因”。比如创建用户时,参数不合法就返回 400,附带 {"code":"USERNAME_EMPTY","message":"用户名不能为空"} 这样的结构;用户已存在就返回 409,带上 {"code":"USER_EXISTS"}。前端只需要先判断 response.status,如果落在 2xx 就正常处理,落在 4xx/5xx 再读取业务错误码做细分提示。
状态码的选择也要贴合语义。成功创建资源用 201;成功但无返回体用 204;资源不存在用 404;请求方法不受支持用 405;校验失败用 422 还是 400 团队里可以约定,但一旦定了就不要变。
4.2 前端拿到状态码后该怎么处理
前端处理状态码时,最容易踩的坑是不知道 fetch 默认不会把 404、500 当成 reject。fetch 只有在网络层失败(断网、DNS 解析失败)时才会 reject,HTTP 状态码是 404 或 500,它照样 resolve。要判断请求是否成功,必须检查 response.ok 或 response.status。这一点和 axios 的行为不一样,axios 在状态码不是 2xx 时会 reject,所以用 axios 的老手切到 fetch 时很容易写错。
前端拦截器里一般会做三件事:
- 2xx:直接放行,把数据交给业务处理。
- 401:清掉本地登录态,跳转到登录页。
- 4xx/5xx:统一弹错误提示,但不要把服务端返回的原文直接怼到用户脸上,应该映射成用户能看懂的语言。
还有一个细节:304 不要当成失败。浏览器协商缓存命中时会返回 304,响应体为空,这是正常的。如果你在拦截器里把所有非 2xx 都当成异常,304 也会被捕获,导致页面数据被清空。需要在拦截器里把 304 单独放行。
4.3 重定向、缓存与状态码的几个坑
301 和 302 的缓存行为差异实际影响很大。301 会被浏览器强缓存,你在服务端改了跳转目标,但用户浏览器可能还在用旧目标,要等缓存过期才生效。所以开发测试重定向时,建议在开发者工具里勾选“Disable cache”,或者临时把 301 改成 302 测试。
304 与缓存头的配合也很值得注意。服务端返回 304 的前提通常是请求带了 If-None-Match 或 If-Modified-Since,服务端校验资源没变后返回 304。如果你在页面里看到明明是 304,但用户反馈数据没更新,多半是缓存策略把资源生命周期设置得太长。调整 Cache-Control 的 max-age 或引入版本号解决。
最后是 410 和 404 的选择。如果某个资源已经永久下线,不要只返回 404,更建议返回 410。原因前面说过,410 告诉搜索引擎“我明确删除了”,搜索引擎会更快移除索引,而不是反复爬取一个 404。这个细节可能只有做 SEO 的人和后端深挖过才会注意到。
5. 完整状态码清单大全(速查版)
这一节把目前协议里比较常见的状态码按分类列一遍。为了阅读方便,我剔除了少数历史遗留且极少遇到的码,剩下的基本够你应付日常开发。
5.1 1xx 信息性响应
1xx 在业务代码里基本见不到,通常由底层网络库消化掉。你只需要知道有这类状态码存在即可。
| 状态码 | 名称 | 含义 |
|---|---|---|
| 100 | Continue | 客户端可以继续发送请求体 |
| 101 | Switching Protocols | 服务器同意切换协议,比如升级到 WebSocket |
| 102 | Processing | WebDAV 场景中表示服务器还在处理,仅历史使用 |
| 103 | Early Hints | 在最终响应前提前返回部分响应头,加速页面加载 |
5.2 2xx 成功
| 状态码 | 名称 | 含义 |
|---|---|---|
| 200 | OK | 请求成功,响应体包含结果 |
| 201 | Created | 资源创建成功 |
| 202 | Accepted | 请求已被接受,但处理还没完成,常用于异步任务 |
| 203 | Non-Authoritative Information | 返回的元信息不是原始服务器来源 |
| 204 | No Content | 成功但无响应体 |
| 205 | Reset Content | 要求客户端重置当前页面表单 |
| 206 | Partial Content | 范围请求成功,视频拖动、断点续传常见 |
| 207 | Multi-Status | 多状态响应,WebDAV 使用 |
| 208 | Already Reported | WebDAV 中某资源已报告过,避免重复 |
| 226 | IM Used | 服务器完成了资源的实例操作,基本冷门 |
5.3 3xx 重定向
| 状态码 | 名称 | 含义 |
|---|---|---|
| 300 | Multiple Choices | 资源有多种呈现方式,客户端可自行选择 |
| 301 | Moved Permanently | 永久重定向 |
| 302 | Found | 临时重定向 |
| 303 | See Other | 通常用于表单提交后,让客户端用 GET 获取结果 |
| 304 | Not Modified | 协商缓存命中,使用本地缓存 |
| 307 | Temporary Redirect | 临时重定向,且保持原先请求方法不变 |
| 308 | Permanent Redirect | 永久重定向,且保持原先请求方法不变 |
307 和 308 与 302、301 的区别在于:前者要求后续请求不能改变 HTTP 方法。比如你 POST 到 /api/order,如果返回 308,客户端应该继续用 POST 请求新地址;而 302 可能被浏览器改成 GET。
5.4 4xx 客户端错误
| 状态码 | 名称 | 含义 |
|---|---|---|
| 400 | Bad Request | 请求错误,参数或格式有问题 |
| 401 | Unauthorized | 未认证或认证失效 |
| 402 | Payment Required | 预留状态码,部分支付场景会用 |
| 403 | Forbidden | 无权限访问 |
| 404 | Not Found | 资源不存在 |
| 405 | Method Not Allowed | 请求方法不被允许 |
| 406 | Not Acceptable | 服务端无法按客户端要求的格式返回 |
| 408 | Request Timeout | 客户端请求超时 |
| 409 | Conflict | 请求与当前资源状态冲突,比如版本冲突 |
| 410 | Gone | 资源已永久删除 |
| 411 | Length Required | 请求没有指定 Content-Length |
| 412 | Precondition Failed | 请求头里的前置条件不满足 |
| 413 | Payload Too Large | 请求体太大 |
| 414 | URI Too Long | URL 太长 |
| 415 | Unsupported Media Type | 不支持的媒体类型 |
| 416 | Range Not Satisfiable | 请求的范围无法满足 |
| 417 | Expectation Failed | 请求头里的 Expect 无法满足 |
| 418 | I'm a teapot | 愚人节彩蛋状态码,表示“我是个茶壶”,幽默用的 |
| 421 | Misdirected Request | 请求被发送到无法处理该请求的服务器 |
| 422 | Unprocessable Entity | 请求格式正确,但存在语义或校验错误,常用于表单校验 |
| 423 | Locked | 资源被锁定 |
| 424 | Failed Dependency | 当前请求依赖的上一个请求失败 |
| 425 | Too Early | 服务器不愿意处理可能被重放的请求 |
| 426 | Upgrade Required | 客户端需要切换协议,比如升级到 HTTP/2 |
| 428 | Precondition Required | 请求需要带前置条件,防止条件竞争 |
| 429 | Too Many Requests | 请求太频繁,被限流 |
| 431 | Request Header Fields Too Large | 请求头太大 |
| 451 | Unavailable For Legal Reasons | 因法律原因不可用,通常表示内容被屏蔽 |
4xx 里 418 是比较特殊的存在,它来自 1998 年愚人节的 RFC 笑话,后来被正式收录,但实际上没有服务会真的返回它。你可以当成面试彩蛋记住。
5.5 5xx 服务端错误
| 状态码 | 名称 | 含义 |
|---|---|---|
| 500 | Internal Server Error | 服务器内部错误 |
| 501 | Not Implemented | 服务器不支持请求的功能 |
| 502 | Bad Gateway | 网关或中间服务器收到上游无效响应 |
| 503 | Service Unavailable | 服务暂时不可用 |
| 504 | Gateway Timeout | 网关等待上游超时 |
| 505 | HTTP Version Not Supported | 服务器不支持请求的 HTTP 版本 |
| 506 | Variant Also Negotiates | 服务器内部配置错误导致内容协商失败 |
| 507 | Insufficient Storage | 服务器存储空间不足 |
| 508 | Loop Detected | 服务器检测到无限循环 |
| 510 | Not Extended | 客户端需要扩展请求才能继续 |
| 511 | Network Authentication Required | 需要网络认证,常见于公共无线网络强制登录 |
5xx 里 508 值得多说一句,它通常出现在重定向或请求链路配置成环的时候。如果你看到 508,不要急着怪网关,先检查是不是自己把回调地址配置成了循环调用。
状态码这个东西,说难不难,说简单也容易翻车。我个人的习惯是:不在脑子里硬背几十个状态码,但会把分类规则和最常见的十几个码记牢,剩下的靠一张速查表兜底。遇到问题时,先看大类,再结合响应体和日志定位,九成以上的状态码问题都能在几分钟内解决。你手边也可以备一份类似本文的清单,出错的时候翻一翻,比反复试错效率高得多。多排查几次,这些码自然会刻进你的工作习惯里。
