HTTP 状态码速查表:每个代码全解析(1xx-5xx)
打开 DevTools,Network 面板红了一半。同一个接口在生产环境返回 502,本地却是 200,Slack 上同事又问:“这个该是 401 还是 403?” HTTP 状态码三位数字、五个分类,看似简单,选错却会泄漏信息、破坏 SEO,让值班变噩梦。
本文是一份面向一线开发者的完整 HTTP 状态码速查表。一张涵盖生产常见状态码的速查表;针对易混淆组合(301 vs 302、401 vs 403、404 vs 410、502 vs 504)的决策矩阵;一个工具章节,演示如何用 curl、fetch 和 Python requests 检查状态码。下文每个状态码都基于 RFC 9110(当前 HTTP 语义标准)和 IANA HTTP Status Code Registry。
速查表:所有 HTTP 状态码一览
下面是生产环境会真正遇到的状态码,按分类整理。容易踩坑的几组,后文会逐一展开。
| 代码 | 名称 | 何时会遇到 |
|---|---|---|
| 100 | Continue | 带 Expect: 100-continue 发送大体积 POST |
| 101 | Switching Protocols | WebSocket 握手、HTTP/2 升级 |
| 103 | Early Hints | 服务器在正式响应前先推送 Link 头 |
| 200 | OK | GET、PUT、PATCH 的默认成功 |
| 201 | Created | POST 创建了资源(带 Location 返回) |
| 202 | Accepted | 异步任务已入队,尚未完成 |
| 204 | No Content | DELETE 成功、PUT 不需要返回响应体 |
| 206 | Partial Content | 范围请求、视频拖动、断点续传 |
| 301 | Moved Permanently | 旧 URL 永久退役,搜索引擎转移权重 |
| 302 | Found | 临时重定向,原 URL 仍是规范地址 |
| 303 | See Other | 表单 POST 后的 Post/Redirect/Get 模式 |
| 304 | Not Modified | 条件 GET,ETag 或 If-Modified-Since 命中 |
| 307 | Temporary Redirect | 类似 302,但保留方法和请求体 |
| 308 | Permanent Redirect | 类似 301,但保留方法和请求体 |
| 400 | Bad Request | JSON 格式错误、缺必填字段、Schema 校验失败 |
| 401 | Unauthorized | 没有凭证或令牌过期 |
| 403 | Forbidden | 已认证但无权限 |
| 404 | Not Found | 资源不存在(或被刻意隐藏) |
| 405 | Method Not Allowed | 向只支持 GET 的端点发 POST(必须带 Allow) |
| 408 | Request Timeout | 客户端发送请求耗时过长 |
| 409 | Conflict | 乐观锁失败、唯一键冲突 |
| 410 | Gone | 资源已被永久删除,不会再回来 |
| 415 | Unsupported Media Type | Content-Type 错误,比如向 JSON API 发 XML |
| 422 | Unprocessable Content | 语法没问题,语义校验失败 |
| 425 | Too Early | TLS 1.3 早期数据有重放风险 |
| 428 | Precondition Required | 服务器要求带 If-Match 防止丢失更新 |
| 429 | Too Many Requests | 触发限流(必须带 Retry-After) |
| 451 | Unavailable for Legal Reasons | DMCA、GDPR 删除、地区屏蔽 |
| 500 | Internal Server Error | 代码里有未捕获的异常 |
| 501 | Not Implemented | 不支持该方法或特性(REST 中罕见) |
| 502 | Bad Gateway | 上游返回了非法响应 |
| 503 | Service Unavailable | 维护中或过载 |
| 504 | Gateway Timeout | 上游没及时返回 |
| 507 | Insufficient Storage | WebDAV 磁盘空间不足 |
| 508 | Loop Detected | WebDAV 出现无限重定向或递归 |
| 511 | Network Authentication Required | 酒店、机场 WiFi 的强制门户 |
下面按分类展开,配决策矩阵、反模式案例,以及选错状态码带来的 SEO 后果。
HTTP 状态码工作原理(三位数字的解剖)
为什么是三位数字?
HTTP 状态码采用三位十进制数字,源自 HTTP/0.9 的设计需求:信号要定长,便于解析器快速分支判断;又要留足空间容纳新状态码。三位数字给了 900 个可用值(100-999),完全够用,IANA 注册表至今也只用到 60 个左右。
第一位数字代表分类,第二位和第三位则是该分类内的具体编码。如果客户端不认识 418,应当回退按通用 4xx 处理。RFC 9110 §15 对此有明确规定:客户端必须把无法识别的状态码当作所属分类的 x00 处理。
五大分类速览
| 分类 | 含义 | 是否必须有响应体? | 默认是否可缓存? |
|---|---|---|---|
1xx | 信息性,临时响应,后续还会有内容 | 否 | 否 |
2xx | 成功,请求已被理解和接受 | 通常需要 | 取决于方法 |
3xx | 重定向,需要进一步操作 | 可选 | 301、308 是;302、307 否 |
4xx | 客户端错误,你的问题,改请求 | 是(需说明) | 通常否 |
5xx | 服务端错误,我们的问题,重试可能有效 | 是(需说明) | 否 |
「默认是否可缓存」这一列要留意。CDN 和浏览器会激进地、永久地缓存 301 和 308,生产环境里选错重定向码很难撤销,因为用户本地已经把重定向缓存死了。SEO 章节会再次提到这一点。
想深入理解 URL 结构(重定向码就是围绕 URL 工作的),URL 编码与解码:百分号编码开发实战指南会从百分号编码、查询字符串讲到字节级处理流水线,逐层讲清是什么决定了一个 URL 是否合法。
1xx——信息性响应(你究竟何时会遇到)
大多数开发者好几年都未必直接见到 1xx。它们是临时响应,服务器在告诉客户端”我还在,继续”。浏览器 DevTools 通常会隐藏,多数 HTTP 库会把它们折叠进最终响应里。
下面每个状态码,MDN 的 HTTP 响应状态参考都是友好的二次校验来源,定义存疑时不妨对照查阅。
100 Continue
客户端在请求头中发送 Expect: 100-continue,先停下来再发送大体积请求体。服务器若愿意接收,回 100 Continue;若打算拒绝,则回 4xx。这能省下大块上传带宽:服务器要因为缺一个头部就拒绝请求时,没必要先把 200 MB 数据传过去。
curl -v -H "Expect: 100-continue" \
-H "Content-Type: application/octet-stream" \
--data-binary @big-file.bin \
https://api.example.com/upload
如果你在详细输出里没看到 < HTTP/1.1 100 Continue,多半是客户端把这个头部剥掉了,或服务器不支持。
101 Switching Protocols
把 HTTP 连接升级为 WebSocket 或 HTTP/2 连接的握手过程。客户端发送 Upgrade: websocket,服务器回 101 Switching Protocols,从那一刻起这条连接走的就是另一种协议。在任何聊天应用、实时面板或协作工具的 Network 面板里都能看到。
103 Early Hints
一个相对较新的状态码(RFC 8297,2017 年),允许服务器在主响应就绪之前先发送 Link 头作为预加载提示。浏览器在服务器还在渲染时就开始拉取 CSS 和 JS。截至 2026 年,Cloudflare、Fastly、Vercel 都已在生产环境支持 103,它是 HTTP/2 服务器推送(已在 Chrome 中废弃)的现代替代方案。
HTTP/1.1 103 Early Hints
Link: </styles.css>; rel=preload; as=style
Link: </app.js>; rel=preload; as=script
HTTP/1.1 200 OK
Content-Type: text/html
...
反模式排查:预期能看到 1xx 但客户端始终收不到,问题通常出在反向代理上。较旧版本的 nginx 会把 Expect: 100-continue 和 103 Early Hints 直接剥掉。怀疑服务端坏掉之前先检查代理配置。
2xx——成功(远不只 200)
凡事都返回 200 OK 是 REST API 里最常见的代码味道。2xx 家族携带的语义信息能让客户端更聪明、缓存更高效。
200 OK
默认值。GET 返回资源,PUT 返回更新后的资源(或 204),PATCH 返回打了补丁的资源。没有理由用更具体的状态码时,就用 200。
201 Created
创建新资源的 POST 应当返回 201,并附带指向新资源的 Location 头。RESTful 客户端正是通过这个头部来发现刚刚创建的资源的规范 URL。
HTTP/1.1 201 Created
Location: /api/users/42
Content-Type: application/json
{"id": 42, "name": "Ada Lovelace"}
202 Accepted
服务器收下了请求,但还没处理完。适用于异步任务,客户端应当轮询、订阅 webhook 或访问状态接口。响应体里通常带一个 job ID。
204 No Content
成功,但没有响应体。常见于 DELETE(资源都没了,还能返回什么)和那些客户端已知新状态的 PUT。表单提交返回 204,浏览器不会切换当前页面,单页应用中那种「发完就不管」的操作很有用。
206 Partial Content
范围请求的响应:客户端用 Range: bytes=1000-2000 请求第 1000 到 2000 字节,服务器只返回这一段。视频流、断点续传、基于 HTTP 的文件同步都依赖 206。
决策:POST 该用 200、201 还是 204
| 场景 | 状态码 | 响应体 |
|---|---|---|
| POST 创建了新资源 | 201 Created | 新资源(或仅 ID)+ Location |
| POST 触发了异步任务,结果未就绪 | 202 Accepted | Job ID、轮询 URL |
POST 是动作而非资源操作(如 /login) | 200 OK | 动作结果(令牌、状态) |
| POST 成功但响应体为空 | 204 No Content | (无) |
在 200 和 201 之间犹豫时,问自己:“服务器是不是创建了一个有自己 URL 的资源?“是,用 201;否则 200。
3xx——重定向(301 vs 302 vs 307 vs 308)
重定向是被误用最多的分类。301、302、307、308 之间的差异,可以归结为三个相互独立的问题:迁移是否永久?方法是否保留?响应是否可缓存?
301 Moved Permanently
资源已经搬走,再也不会回来。搜索引擎会把链接权重转移到新 URL。浏览器和 CDN 会无限期缓存 301:用 301 把 /old 重定向到 /new,事后想反悔,已经缓存重定向的用户就会一直被送到 /new(直到他们清缓存为止)。
历史上,浏览器在收到 301 时可能会改写请求方法(POST → GET),HTTP/1.1 引入 308 就是为了解决这个问题。
302 Found
临时重定向。原 URL 仍是规范地址,搜索引擎应继续索引原地址。适用于 A/B 测试路由、维护页、「请先登录」流程。
和 301 一样,浏览器历史上也会在 302 上把 POST 改写成 GET。如果你需要重定向 POST 并保持 POST,请改用 307。
303 See Other
总会把方法改写为 GET。Post/Redirect/Get 模式:表单 POST 到 /submit,服务器返回 303 配 Location: /thank-you,浏览器执行 GET /thank-you。刷新感谢页不会再次提交表单。303 就是为这个场景设计的。
304 Not Modified
条件响应。客户端发送 If-None-Match: "abc123"(或 If-Modified-Since),服务器检查资源是否变化,没变就返回 304,不带响应体。浏览器使用本地缓存副本。每一层 CDN 和缓存正是靠这个机制把网站维持得飞快。
307 Temporary Redirect
类似 302,但方法不能改。POST 仍是 POST,请求体保留。需要在非 GET 请求上做临时重定向时用它。
308 Permanent Redirect
类似 301,但方法不能改。对接受 POST/PUT 的 API 来说,是更现代、更安全的永久重定向方案。
决策矩阵:该用哪个重定向码?
| 永久(永久缓存) | 临时(不缓存) | |
|---|---|---|
| 方法可以改成 GET | 301 Moved Permanently | 302 Found |
| 方法必须保持不变 | 308 Permanent Redirect | 307 Temporary Redirect |
特例:如果就是要把 POST 改成 GET(Post/Redirect/Get 模式),用 303 See Other。
带浏览器导航的 HTML 页面用 301 和 302 一般没问题,因为本来就是 GET。但 API 和表单更建议用 308 和 307,避免方法被悄悄改写。
4xx——客户端错误(如何选对)
4xx 表示客户端做错了事。4xx 的词汇越丰富,API 用起来越顺手:客户端可以基于状态码分支判断,而不必去解析错误字符串。
400 Bad Request
通用语法错误。JSON 格式错误、结构层面缺必填字段、服务器根本无法解析的请求。如果请求能解析但业务校验失败,更适合用 422。
401 Unauthorized 与 403 Forbidden
HTTP 中最容易混淆的一对。看清差异后并不难:
401 Unauthorized:请求缺少有效身份凭证。服务器不知道你是谁。再次发送凭证(或刷新令牌)也许能解决。响应必须按 RFC 9110 §15.5.2 包含WWW-Authenticate头。403 Forbidden:服务器知道你是谁,仍然拒绝。再发一次也没用。需要换一组凭证或获取额外权限。
| 你看到 | 真实情况 |
|---|---|
401 带 WWW-Authenticate: Bearer | 没有令牌、令牌过期或令牌无效 |
登录成功后出现 403 | 已登录,但当前用户无权访问该资源 |
登录成功后出现 401 | Bug,你大概率想要的是 403 |
反模式:用 403 替代 404。一些站点对未登录用户访问 /admin/dashboard 返回 403,这等于泄漏了 /admin/dashboard 的存在。GitHub 的做法是:你不是成员的私有仓库一律返回 404,从你的视角看资源「不存在」。这是有意识的信息隐藏策略,不是 bug。
404 Not Found 与 410 Gone
两者都在说”这里没这个资源”,区别在于永久性和 SEO 影响。
404 Not Found:可能存在,可能不存在,可能还会回来。搜索引擎会持续来探。410 Gone:曾经存在,被刻意删除,不会再回来。搜索引擎会更快从索引中剔除。
想让被删的商品页立刻从 Google 索引里消失,就用 410。只是 URL 临时坏了,404 就够。
405 Method Not Allowed
URL 存在,但不接受当前方法。响应必须包含列出受支持方法的 Allow 头。
HTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
{"error": "POST is not allowed on this endpoint"}
漏写 Allow 头是手写 REST API 里第一名的契约违规。
408 Request Timeout
客户端开始发送请求后突然没声音了,服务器等不下去就放弃。和 504 Gateway Timeout 不同:504 是上游慢,408 是客户端自己太慢。
409 Conflict
请求与当前状态冲突。最常见用法是乐观锁:客户端发送 If-Match: "etag-v3",但服务端当前 ETag 是 "etag-v4",更新被驳回,返回 409。
410 Gone
见上文,永久删除。适合从搜索索引中移除已软删除的记录。
415 Unsupported Media Type
客户端发了服务器不认识的请求体。向只接受 JSON 的 API 发送 XML,就会拿到 415。响应应当提示可接受的类型。
422 Unprocessable Content
请求能正常解析,但语义校验不过。RFC 9110 在 2022 年终于把它从 WebDAV 升级进核心规范。校验错误就用 422:
{
"error": "validation_failed",
"details": [
{"field": "email", "message": "must be a valid email"},
{"field": "age", "message": "must be at least 13"}
]
}
API 在 400 和 422 之间难以抉择时,规则是:解析不了用 400,解析得了但语义不通用 422。
425 Too Early
服务器不愿冒险处理可能是 TLS 1.3 早期数据重放的请求。主要影响 CDN 和反向代理。
428 Precondition Required
服务器坚持要求客户端发送 If-Match 或 If-Unmodified-Since,以避免丢失更新问题。常用于协同编辑类 API。
429 Too Many Requests
触发限流。响应必须带 Retry-After(秒数或 HTTP 日期),让规范的客户端知道该退避多久。
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json
{"error": "rate_limited", "limit": 100, "window": "1m"}
451 Unavailable for Legal Reasons
数字「451」致敬布拉德伯里的《华氏 451》。但用例并非虚构:DMCA 删除、GDPR 被遗忘权请求、国家级地理封锁,都适用 451。响应应按 RFC 7725 包含 Link 头,指向要求屏蔽的法律主体。
418 I’m a Teapot(彩蛋)
这状态码是真的。RFC 2324 是 1998 年愚人节玩笑,但因为太多产品认真实现了它,IETF 索性把它留在了规范里。别真的把 418 用到生产 API 上,大多数反向代理和负载均衡处理不好它。
决策矩阵:该用哪个 4xx?
| 场景 | 状态码 |
|---|---|
| 请求体格式错误或无法解析 | 400 |
| 没有身份认证 / 令牌过期 | 401 |
| 已认证但无权限 | 403 |
| URL 不存在(或被刻意隐藏) | 404 |
| URL 曾经存在,被刻意删除 | 410 |
| 错误的 HTTP 方法 | 405(带 Allow) |
错误的 Content-Type | 415 |
| 乐观锁冲突 | 409 |
| 校验错误(解析得了但通不过校验) | 422 |
| 限流 | 429(带 Retry-After) |
| 因法律原因屏蔽 | 451 |
5xx——服务端错误(究竟哪里坏了)
5xx 是「我们的锅」。值班工程师最关心是哪个 5xx 把自己凌晨三点叫醒,因为状态码会告诉你先去查哪一层。
500 Internal Server Error
万能兜底。几乎一定是未捕获的异常冒泡到框架的默认处理器。它对原因毫无暗示,所以结构化日志比状态码本身更重要。
501 Not Implemented
服务器根本不支持该方法。和 405 不同:405 是「这个 URL 不允许此方法」,501 在说”本服务器压根不知道 PROPFIND 是什么”。在 REST API 中很少见。
502 Bad Gateway
反向代理或负载均衡从上游收到了非法响应。上游确实回复了,但内容是垃圾:协议错误、响应头格式错乱、响应中途断连。从 CDN 看到 502,源站很可能在崩溃或返回截断的响应体。
503 Service Unavailable
服务器有意暂时不接收请求。适用于维护窗口或优雅过载响应,应当带 Retry-After。
504 Gateway Timeout
反向代理等上游响应一直没等到。上游变慢或卡住,和 502 不同:502 是上游回了垃圾。
502 vs 504:值班诊断
| 你看到 | 第一步检查 |
|---|---|
502 Bad Gateway | 上游返回了非法数据,查源站日志,看是否崩溃、响应残缺、协议不匹配 |
504 Gateway Timeout | 上游卡死,查源站 CPU、数据库查询、下游 API 调用,以及代理的 proxy_read_timeout |
常见的混淆:一条耗时 60 秒的数据库查询,代理 30 秒超时就显示 504;应用服务器 90 秒超时抛异常就显示 500。同一个根因,不同的状态码、不同的日志行,监控面板要把两者都拎出来。
507 Insufficient Storage
WebDAV 专用。服务器磁盘已满。若非 WebDAV API 也返回这个,那是有人在硬塞含义。
508 Loop Detected
WebDAV PROPFIND 操作中出现无限递归。极其罕见。
511 Network Authentication Required
强制门户专用:酒店或机场的 WiFi 用 511 告诉你浏览器「先登录门户页面」。响应里会带一个指向门户页的 Location。
排错矩阵:先排查哪一层
| 状态码 | 应用 | 代理 | 数据库 | 网络 |
|---|---|---|---|---|
500 | 是 | — | 可能(未捕获的数据库错误) | — |
502 | — | 是(上游响应畸形) | — | 可能(TCP 重置) |
503 | 是(维护开关) | 是(限流拒绝) | — | — |
504 | 是(处理慢) | 是(超时配置) | 是(慢查询) | 是(DNS、丢包) |
HTTP 状态码常见反模式
下面这五个错误,几乎包揽了我代码评审里看到的大部分糟糕实现。
1. 把错误包在 200 OK 里返回
HTTP/1.1 200 OK
{"success": false, "error": "user_not_found"}
每一个监控工具、CDN、缓存层从此都以为请求成功了。重试逻辑失灵,懂状态码的负载均衡会把坏流量转给「健康」后端。这种模式起源于 JSON-RPC,被 GraphQL 沿用,GraphQL 这么做是因为部分成功需要逐字段错误报告,情有可原。REST 没有借口:客户端错误用 4xx、服务端错误用 5xx,结构化细节放到响应体里。
2. 401 和 403 混用
401 和 403 用得不一致,攻击者就能通过探测 API 推断哪些资源存在。定一个策略:要么对「你看不到这个」一律返回 404(GitHub 私有仓库的做法),要么始终返回 403。不一致本身就是信息泄漏。
3. 用 404 掩盖 403
有时正确,常常是 bug。GitHub 对私有仓库返回 404 是有意为之,仓库的存在本身就是敏感信息。但如果你的 API 对「该账号已被封禁」也返回 404,正常用户就分不清自己是输错了用户名还是被封了。把策略写明白,并贯彻一致。
4. 把 500 当默认兜底
各种框架都让这件事很容易,问题正在于此。每一个未捕获的异常都变成 500,告警系统就分不清是「数据库挂了」还是「用户传了个格式错乱的 UUID」。校验错误要捕获并抛 400 或 422;ORM 抛的 NotFound 要捕获并抛 404。500 留给真正出乎意料的故障,抛 500 时还要带请求 ID,方便日后关联排查。
5. 重定向链过长
每一跳都要一次往返。/old → /intermediate → /canonical,最坏情况要多两次 DNS 查询和两次 TCP 握手。Google 会专门下调超过 3 跳重定向链的爬取优先级;浏览器一般在约 20 跳后切断重定向链以防死循环。要在源头收拢,CDN 配置里改,或者在应用的重定向映射里改。
HTTP 状态码与 SEO
搜索引擎把状态码当作权威信号,决定一个 URL 是保留、丢弃还是迁移。选错了,排名就跟着动。
301 vs 302(链接权重)
301 Moved Permanently 会传递 PageRank:Google 会把所有指向旧 URL 的信号都视为新 URL 的,把新 URL 当作规范地址。302 Found 不传递链接权重(或根据 Google 启发式判断缓慢传递)。某个 URL 是永久改名了,用 301;把游客重定向到 /login,用 302。
404 vs 410 vs 软 404
Google 区分三种「不存在」的状态:
404 Not Found:Google 会定期回访,URL 会在索引中保留一段时间。410 Gone:Google 剔除 URL 更快,往往一个抓取周期内就处理完。- 软 404:Google 用来形容那些返回
200 OK但内容显示「未找到」的页面。Google 会基于内容模式自动检测,并照样按404处理;但你已经浪费了一次抓取配额,还可能稀释真实内容。
清理过期索引时,对真正永久删除的 URL 一定要返回真正的 410。
5xx 与抓取预算
当一个站点持续返回 5xx,Google 爬虫会主动降低抓取频率。Search Console 的「Crawl Stats」报告里能看到这一点:5xx 错误持续飙升,可能让抓取预算掉好几天,新页面的入索引时间也会跟着拖长。把 5xx 比例既当可靠性指标,也当 SEO 指标。
实际坏掉但写着 200 OK
返回 200 OK 配错误页面(即软 404 反模式)是 SEO 最糟的情况。Google 会去索引那条错误信息,给它一个根本排不上的关键词,然后慢悠悠地才意识到这页坏了。哪怕你的单页应用要渲染一个友好的错误界面,服务器也必须返回正确的状态码。
如何检查 HTTP 状态码(工具篇)
看不见就修不了。下面三种工具,每位活跃的开发者至少要熟练掌握一种。
浏览器 DevTools 的 Network 面板
Chrome、Firefox、Safari 都会在 Network 标签页显示 Status 列。看不到 Status Text,右键列头加上即可。几个实用技巧:
- Preserve log:跨页面导航保留记录,便于完整观察重定向链。
- 按状态码过滤:Chrome 中输入
status-code:5xx就能只看服务端错误。 - Replay XHR:任意请求右键 → Replay XHR,无需刷新页面即可重发。
对于重定向,展开请求即可看到每一跳和对应的状态码。
curl(万能解法)
curl 什么都能看到。下面这几种模式能解决 90% 的调试问题:
# 仅查看状态码
curl -o /dev/null -s -w "%{http_code}\n" https://api.example.com/users/1
# 仅查看响应头(HEAD 请求,跟随重定向)
curl -I -L https://example.com
# 完整 verbose 输出,含请求和响应头
curl -v https://api.example.com/users/1
构造测试 URL、查询字符串里有特殊字符时,用 --data-urlencode 让 curl 自动处理编码,或者把 URL 粘贴到URL 编码解码工具里,确认实际发到线上的字节是什么。
# curl 会自动对查询值进行编码
curl -G "https://api.example.com/search" \
--data-urlencode "q=hello world & friends"
# 实际发送:GET /search?q=hello%20world%20%26%20friends
JavaScript fetch
Response.status 属性是状态码整数;Response.ok 在任何 2xx 时为 true。
const res = await fetch('https://api.example.com/users/1');
console.log(res.status); // 200
console.log(res.statusText); // "OK"
console.log(res.ok); // true
if (!res.ok) {
if (res.status === 401) {
// 刷新令牌后重试
} else if (res.status === 429) {
const retryAfter = Number(res.headers.get('Retry-After')) || 1;
await new Promise(r => setTimeout(r, retryAfter * 1000));
} else if (res.status >= 500) {
throw new Error(`Server error: ${res.status}`);
}
}
axios 中同样的逻辑放在拦截器里:
import axios from 'axios';
axios.interceptors.response.use(
response => response,
error => {
const status = error.response?.status;
if (status === 401) {
// 跳转到登录页
}
return Promise.reject(error);
}
);
Python requests
import requests
r = requests.get('https://api.example.com/users/1')
print(r.status_code) # 200
print(r.reason) # 'OK'
# 4xx/5xx 时抛 requests.exceptions.HTTPError
r.raise_for_status()
# 手动处理
if r.status_code == 429:
retry_after = int(r.headers.get('Retry-After', '1'))
time.sleep(retry_after)
elif 500 <= r.status_code < 600:
raise RuntimeError(f'Server error: {r.status_code}')
raise_for_status() 是 Python 处理「4xx/5xx 直接抛错」的惯用法。脚本里希望出错就抛异常、不想手动分支判断 status_code 时,就用它。
Postman 与 Bruno
两者都允许在测试脚本里对状态码做断言:
// Postman/Bruno test script
pm.test("Status is 201", () => {
pm.response.to.have.status(201);
});
pm.test("Has Location header", () => {
pm.expect(pm.response.headers.get('Location')).to.match(/^\/users\/\d+$/);
});
把这些断言放进 CI,拿预发环境跑,上生产前就能发现契约违规。
FAQ
401 和 403 有什么区别?
401 Unauthorized 表示服务器不知道你是谁,凭证缺失、过期或无效。403 Forbidden 表示服务器知道你是谁,但仍然拒绝。换一组凭证可能解决问题,用 401;换了也没用,用 403。
什么时候该用 301,什么时候该用 302?
迁移是永久性的(旧 URL 永远不回来,希望搜索引擎把链接权重转移到新 URL)用 301。临时重定向(登录流程、A/B 测试、维护页等)原 URL 仍是规范地址时,用 302。API 更建议用 308 和 307,因为它们会保留请求方法。
502 Bad Gateway 错误是什么意思?
502 表示反向代理或负载均衡从上游服务器收到了非法响应。上游确实回了,但内容是垃圾:协议错乱、响应头畸形或响应中途断连。它和 504 Gateway Timeout 不同,后者是上游根本没回。第一时间查源站日志,看是否崩溃或响应被截断。
什么是「软 404」?
「软 404」是指页面返回 200 OK,但内容上是「未找到」。Google 会启发式地识别并照 404 处理。它会浪费抓取预算,还可能稀释真实内容。即便单页应用要渲染一个友好的错误界面,服务器也必须返回真正的 404 或 410。
什么时候用 422 而不是 400?
服务器根本无法解析请求时(JSON 格式错误、缺结构性字段、语法错误)用 400 Bad Request。请求能正常解析但业务校验不过(邮箱格式不对、值越界、字段语义自相矛盾)用 422 Unprocessable Content。一句话总结:400 管语法,422 管语义。
收到 429 Too Many Requests 该如何应对?
读取 Retry-After 头(秒数或 HTTP 日期),至少退避这么久再重试。没有 Retry-After 时,从 1 秒起做带抖动的指数退避。绝不要立刻重试,那是封号的最快方式。
2026 年了,1xx 信息性状态码还在用吗?
是的,但绝大多数对应用代码不可见。100 Continue 和 101 Switching Protocols 是 HTTP/1.1 的基础特性。103 Early Hints 越来越多被 Cloudflare、Fastly、Vercel 用于在主响应前推送预加载提示,能明显改善 Largest Contentful Paint 指标。多数 HTTP 库会把 1xx 折叠进最终响应,因此通常只在 DevTools 或 curl -v 里能看到。
418 「I’m a teapot」是真的状态码吗?
意外地,是。RFC 2324 是 1998 年的愚人节玩笑,但因为足够多的产品真把它实现了,IETF 在 RFC 7168 里把它保留下来。生产环境别用 418:很多反向代理和负载均衡处理不好它,而且它在玩笑之外没有任何实际用途。