从零开始认识网络状态码

本文摘要本文是一份详尽的 HTTP 状态码全指南,系统性梳理了从 100​ 到 505​ 的核心协议代码。文章摒弃枯燥的 RFC 文档语言,采用生动的类比(如“快递包裹”、“门禁系统”)解析了 1xx 至 5xx 五大类状态码的深层含义与应用场景。内容特别聚焦于开发者常踩的坑,如 401/403 权限边界、502/504 网关故障及 307/308 重定向陷阱。针对每一个状态码,均提供了面向前端、后端及运...

什么是HTTP状态码

当我们每天在浏览器中输入网址、点击链接时,看似简单的页面加载背后,实则是一场发生在分秒之间的“对话”。这场对话的发起者是浏览器(客户端),而应答方则是远端的服务器。每次请求时,服务器在传回网页数据前,总会先发送一个包含HTTP状态码的信息头。这个状态码,就是服务器对浏览器请求的第一句、也是最关键的“回应”。它用三个数字,简洁地告诉我们请求是成功了、失败了,还是需要进行下一步操作,是理解网络通信状况的基石。


状态码 100 Continue

在 HTTP 协议体系中,100 Continue 属于 1xx 信息类状态码,其核心作用是让服务器在接收客户端请求体之前,先对请求头进行“预审”。这是一种用于优化传输效率和避免资源浪费的机制。

当客户端准备发送较大的数据包(如大文件上传、复杂表单提交)时,如果直接发送完整数据,一旦服务器拒绝(如权限不足、格式错误),就会造成带宽浪费。为此,客户端可以在请求头中加入 Expect: 100-continue。此时,服务器若检查通过,会立即返回 100 Continue,相当于告诉客户端:“头信息没问题,请继续发送数据。”

这种机制在高并发或大文件场景中尤为重要。它不仅能减少无效数据传输,还能让客户端快速获得反馈,及时终止错误请求。需要注意的是,100 Continue 是协议层的中间状态,普通用户在前端页面无法看到,仅出现在开发者工具或服务器日志中。此外,随着 HTTP/2 和 HTTP/3 的发展,由于协议帧结构的改进,100 Continue 的使用频率有所下降,但在传统 HTTP/1.1 环境中依然常见。

通常解决方案

客户端:现代 HTTP 库一般自动处理 100 Continue,无需手动干预;如遇兼容问题,可尝试移除 Expect头。

服务端:主流 Web 服务器(Nginx、Apache)默认支持;若业务简单,可直接跳过该机制以降低延迟。

调试:请求卡顿时检查 Network 面板,确认是否被代理或防火墙拦截。


状态码101 Switching Protocols

HTTP 101 状态码,全称为 Switching Protocols(切换协议),是服务器告知客户端:“我同意你提出的协议升级请求,咱们换个规则继续聊。”

这一机制最常见于 WebSocket 的建立过程。当客户端希望在同一个 TCP 连接上从 HTTP 协议切换到 WebSocket 时,会在请求头中加入 Upgrade: websocket和 Connection: Upgrade。如果服务器支持且允许,就会返回 101 Switching Protocols,随后双方停止 HTTP 通信,开始使用新的协议进行双向数据传输。

除了 WebSocket,HTTP/2 的升级协商在早期版本中也曾使用类似方式。101 的意义在于它打破了单一协议的限制,让连接更加灵活高效。需要注意的是,101 同样属于 1xx 信息类状态码,它只是一次“过渡”,不会直接展示给用户,而是隐藏在浏览器的开发者工具或网络抓包中。

通常解决方案

  • 客户端:确保正确发送 UpgradeConnection 头;使用成熟库(如 wsSocket.IO)处理握手与状态。
  • 服务端:在 Nginx 等反向代理中开启 WebSocket 支持,避免代理阻断协议升级。
  • 调试:若连接失败,优先检查响应头是否包含 101,以及 Upgrade 值是否匹配。

状态码 200 OK

HTTP 200 是最常见、也是最广为人知的状态码,意为 OK(成功)。它表示客户端的请求已被服务器成功接收、理解并处理,且响应中包含了请求所期望的结果。简单来说,这就是服务器在告诉你:“一切顺利,你要的东西拿好了。”

虽然 200 看起来只是一个简单的“成功”信号,但它在不同场景下承载的数据截然不同。例如,当你访问网页时,200 意味着服务器成功返回了 HTML 文档;当你调用 API 获取数据时,它可能返回 JSON 或 XML 格式的业务数据;而在提交表单(POST 请求)后,200 也可能表示服务器已成功处理了提交的数据。

值得注意的是,200 属于 2xx 成功类状态码 的“基准线”。在这个家族中,还有 204 No Content(成功但无返回体)和 206 Partial Content(返回部分内容,常用于断点续传)。在实际开发中,区分这些细微差别非常重要,因为前端代码往往需要根据不同的 2xx 代码执行不同的逻辑(例如刷新页面或仅更新局部数据)。

通常解决方案

  • 前端开发:默认处理 200 响应,解析返回的 Body 数据并更新 UI;注意处理空数据或异常数据结构。
  • 后端开发:确保业务逻辑执行成功后返回 200,避免在 200 响应中包含错误信息(应使用 4xx 或 5xx)。
  • 缓存优化:200 响应通常可被浏览器或 CDN 缓存,合理配置 Cache-Control 头以提升加载速度。

状态码 201 Created

HTTP 201 状态码的含义是 Created(已创建)。它表示服务器成功处理了请求,并且由此创建了新的资源。与 200 OK 的通用成功不同,201 具有明确的“因果性”——它不仅是成功,更是“从无到有”的成功。

这一状态码在 RESTful API 设计 中尤为常见。当你向服务器提交数据以创建新条目时(例如注册新用户、发布新文章、创建订单),最规范的返回就是 201。它明确告知客户端:“你要创建的东西我已经搞定了,现在它已经存在于服务器上了。”

为了增强语义,201 响应通常会在响应头中包含 Location 字段。这个字段的值是新创建资源的 URL 地址。例如,当你创建一个 ID 为 123 的用户后,服务器可能会返回 Location: /users/123。这让客户端无需拼接 URL,就能直接通过该地址访问刚创建的资源。

理解 201 的关键在于区分它与 200 的边界:200 侧重于 "完成"(完成了查询或处理),201 侧重于“生成”(生成了新的实体)。在开发实践中,正确使用 201 有助于前端逻辑更清晰地判断后续操作(比如跳转至详情页),同时也提升了 API 的可读性和标准化程度。

通常解决方案

  • API 设计:创建操作(POST)成功后返回 201,并尽量带上 Location 响应头。
  • 前端处理:收到 201 后,通常引导用户跳转到新资源的详情页,或刷新列表数据。
  • 幂等性注意:POST 请求通常不是幂等的,多次调用 201 可能会创建多个资源,需注意防重处理。

HTTP 202 Accepted

HTTP 202 状态码表示 Accepted(已接受)。它传达了一个非常微妙且重要的信息:服务器已经收到了请求,但还没有开始处理它。这就像你去银行办业务,柜员收走了你的材料,告诉你“我们先登记一下,稍后处理”,但这并不意味着业务已经办完。

202 状态码通常用于异步处理场景。当客户端发送的请求需要很长时间才能完成(例如视频转码、批量数据导入、复杂的报表生成),服务器为了避免客户端长时间等待连接超时,会立即返回一个 202 状态码。这表示:“我知道你要干嘛了,任务已经在队列里了,你可以先去忙别的了。”

200 OK(已完成)和 201 Created(已创建)不同,202 并不代表最终处理结果。它只是一个中间确认。要获取最终结果,客户端通常需要配合其他机制,比如轮询(Polling)任务状态接口,或者使用 Webhook 让服务器在处理完成后主动通知客户端。因此,在设计 API 时,使用 202 意味着你必须向开发者明确告知后续的查询路径。

通常解决方案

  • 前端处理:收到 202 后不阻塞用户操作,显示“处理中”状态,并通过定时器轮询任务进度接口。
  • 后端实现:使用消息队列(如 RabbitMQ、Kafka)接收请求并返回 202,后台 Worker 异步消费队列任务。
  • 状态追踪:务必提供配套的“任务查询接口”,让客户端能通过任务 ID 获取当前是成功、失败还是仍在排队。

HTTP 203 Non-Authoritative Information

HTTP 203 状态码的含义是 Non-Authoritative Information(非权威信息)。这个状态码并不常见,它主要用于代理服务器场景。

当请求经过代理服务器(Proxy)时,如果代理服务器成功从源站(Origin Server)获取到了响应,但修改了响应头中的部分内容(例如转换了文档格式、添加了代理特有的头部信息),那么它就不能再返回 200 OK 了,因为这不再是源站的“原版”响应。此时,代理服务器会返回 203,以此告知客户端:“我给你的这份数据是真实的,但头部信息可能经过了我的加工,仅供参考,不是源站的权威原件。”

在现代 Web 开发中,随着 HTTPS 的普及(加密传输使得代理难以修改内容)和透明代理的减少,203 的实际出场率极低。但在某些特定的企业内网环境或历史遗留系统中,它仍作为一种元数据提示存在,提醒客户端不要完全依赖响应头的某些字段(如 Content-LengthContent-Type 可能被代理重写过)。

通常解决方案

  • 客户端:通常当作 200 处理即可,但若业务逻辑高度依赖特定的响应头,需警惕这些头信息可能被篡改过。
  • 运维/代理配置:尽量避免不必要的响应头重写;若必须修改,确保返回 203 以明确状态。
  • 调试:遇到 203 时,检查是否存在中间代理(如公司内网网关、CDN 边缘节点)对数据进行了拦截和修改。

HTTP 204 No Content

HTTP 204 状态码表示 No Content(无内容)。它传达的信息非常精确:服务器成功处理了请求,但不需要返回任何响应体(Response Body)

与返回空白页面的 200 OK 不同,204 在协议层面明确要求禁止包含消息体。这意味着客户端在收到 204 后,浏览器不会刷新页面,也不会跳转,仅仅是知道“操作成功了”。

这种特性使其在前后端分离架构中大放异彩。典型场景包括:

  1. 异步删除:前端发送 DELETE 请求删除一条评论,后端执行成功后返回 204,前端直接在前端 DOM 中移除该元素,无需重新加载页面。
  2. 状态更新:发送 PUT 请求标记邮件为“已读”,后端更新数据库后无需返回具体数据,返回 204 最为合适。
  3. 监控心跳:客户端定期向服务器发送心跳包,服务器只需回应 204 告知“我还活着”,无需携带任何负载,极大节省了带宽。

简而言之,当你的 API 只需要告诉客户端“事情办完了,没啥好说的”时,204 是最佳选择。


HTTP 205 Reset Content

HTTP 205 状态码的含义是 Reset Content(重置内容)。它与 204 No Content 非常相似,都表示服务器成功处理了请求且没有返回响应体,但两者的后续行为指令截然不同。

204 的意思是“搞定,你保持现状就行”;而 205 的意思是“搞定,但请你把当前页面恢复到初始状态”。

这个状态码最常见的应用场景是 Web 表单提交。想象一下,用户在一个输入框中填写了内容并提交(POST 请求)。如果服务器返回 200 或 204,输入框里的文字通常会保留在那里。但如果服务器返回 205,它是在命令浏览器:“请求已成功处理,现在请把表单里的所有输入框清空,让用户重新填写。”

这在设计 RESTful API前后端不分离的旧式网站 中非常有用,特别是当你希望用户在提交数据后立即进行下一次输入(例如扫码枪录入、连续登记)时,205 可以避免烦人的手动清屏或页面刷新,提供更流畅的交互体验。

通常解决方案

  • 前端处理:收到 205 后,执行 JavaScript 代码重置表单(form.reset()),或清空当前组件内的数据模型(Model)。
  • 后端设计:仅在需要用户立即重新输入的场景下使用 205,避免滥用导致用户体验混乱(如误删用户输入)。
  • 兼容性:现代单页应用(SPA)通常自行控制状态,较少使用 205,但在原生 HTML Form 提交中依然有效。

HTTP 206 Partial Content

HTTP 206 状态码代表 Partial Content(部分内容)。它是支持断点续传多线程下载的核心技术,也是视频网站实现“拖拽进度条”的底层原理。

当客户端(如浏览器或下载工具)发现某个文件体积巨大时,它不会傻傻地一次性请求全部数据。相反,它会发送一个带有 Range 请求头的 GET 请求,明确告诉服务器:“我只要这个文件的第 1000 到 2000 字节的部分。”如果服务器支持分段请求,它就会返回 206,并在响应头中使用 Content-Range 标明当前返回的是哪一部分(例如 bytes 1000-2000/5000)。

这种机制带来了巨大的性能优势:

  1. 断点续传:下载中断后,客户端只需请求剩余部分,无需重头再来。
  2. 并行下载:下载工具可以开启多个线程,分别下载文件的不同片段,最后合并。
  3. 流媒体播放:观看视频时,点击进度条的任意位置,浏览器就是通过发送 Range 请求直接获取对应时间点的数据片段。

需要注意的是,206 响应通常包含 Content-RangeContent-Length(指当前片段的长度,而非总文件大小)这两个关键头信息,客户端依靠这些信息进行数据的拼接和校验。

通常解决方案

  • 前端/客户端:实现视频播放器时,利用 Range 头请求特定时间段的数据;下载器需处理分段合并逻辑。
  • 后端/服务器:配置 Nginx 或后端代码支持 Range 请求;对于静态资源,确保 Accept-Ranges: bytes 头存在。
  • 错误处理:若范围请求越界,服务器应返回 416 Range Not Satisfiable,客户端需据此调整请求范围。

HTTP 300 Multiple Choices

HTTP 300 状态码的含义是 Multiple Choices(多种选择)。这是一个比较罕见的重定向类状态码,它代表服务器根据客户端的请求,发现了不止一个可能的资源可以满足需求,因此它把“选择权”交还给客户端,让客户端自己决定用哪一个。

这就好比你去餐厅点了一份“今日特餐”,服务员告诉你:“今天的特餐有牛排、鱼排和素食三种,你具体要哪一个?”服务器(服务员)成功理解了请求(点餐),但因为没有指定具体选项,所以返回 300 让你做决定。

在技术实现上,服务器通常会在响应体中包含一个列表,列出这些可选资源的地址及其格式(例如不同的语言版本、不同的文件类型)。一个常见的应用场景是内容协商(Content Negotiation)。例如,当客户端请求 /report 而没有指定后缀时,服务器可能同时存在 /report.html/report.pdf。此时,服务器可以返回 300,并列出这两个版本的 URI,供客户端或用户点击选择。

然而,在现代 Web 开发中,300 很少被直接使用。大多数服务器更倾向于通过 301(永久重定向)或 302(临时重定向)直接将用户导向最合适的那个版本(通常是基于 IP 地址判断的地区版本,或基于 Accept-Language 头判断的语言版本),以避免增加用户的交互成本。

通常解决方案

  • 客户端处理:通常表现为显示一个索引页面,列出所有可选的资源链接,让用户点击跳转。
  • 服务端配置:尽量避免使用 300,除非确实需要用户手动选择;优先使用 Location 头配合 301/302 进行自动跳转。
  • SEO 注意:搜索引擎对 300 的处理不一致,可能会导致权重分散,建议使用明确的 301 重定向来规范网址。

HTTP 301 Moved Permanently

HTTP 301 状态码代表 Moved Permanently(永久移动)。它是 Web 开发中最重要、最常用的重定向指令之一,用于告知客户端:“你所请求的这个资源,已经永远搬到了一个新的地址。”

当一个 URL 返回 301 时,服务器通常会在响应头中携带 Location 字段,指明新地址。浏览器或爬虫在收到 301 后,会自动跳转到这个新地址,且后续所有对该旧地址的请求,都会直接转向新地址,不再访问旧地址。

301 的核心价值在于 SEO(搜索引擎优化)。搜索引擎(如 Google、百度)会将旧 URL 积累的权重(PageRank)、流量和历史记录,完全转移给新 URL。这对于网站改版、更换域名或规范化 URL(例如将 http 重定向到 https)至关重要。如果不使用 301,搜索引擎会认为这是两个独立的页面,导致权重分散或排名下降。

需要注意的是,301 是永久性的。一旦浏览器缓存了 301 重定向规则,即使服务器端移除了重定向,浏览器在很长一段时间内仍会记住这个跳转,这在调试时有时会引发困扰(需要清除浏览器缓存)。

通常解决方案

  • SEO 优化:网站换域名、HTTP 转 HTTPS 时,务必配置 301 重定向,以继承原有权重。
  • 服务器配置:在 Nginx 中使用 return 301 https://$host$request_uri;;在 Apache 中使用 .htaccess 文件配置。
  • 缓存清理:开发测试时,若 301 跳转异常,记得清除浏览器缓存或开启无痕模式进行测试。

HTTP 302 Found

HTTP 302 状态码的含义是 Found(找到),但在实际应用中,它更常被理解为 Moved Temporarily(临时移动)。它表示服务器要求客户端暂时前往另一个地址获取资源,但未来的请求还应继续使用原来的地址。

可以把 301 和 302 的区别看作“搬家”与“出差”:

  • 301(永久搬家):你常去的那家面馆关门了,搬到了街对面,以后你都去新地址吃。
  • 302(临时出差):那家面馆没搬,只是老板今天去进货了,暂时在隔壁借个灶台做饭,明天老板回来了,你还得回老店吃。

在技术上,302 常用于临时性功能。例如,用户未登录时访问个人中心,服务器返回 302 将其临时跳转到登录页;或者网站正在进行 A/B 测试,将部分用户临时引流到新版页面。

SEO 影响是两者最大的分水岭。对于 302 重定向,搜索引擎会保留原 URL 的索引,不会将权重传递给新地址。如果你错误地使用了 302 代替 301(例如网站永久换域名),会导致搜索引擎认为这只是临时变动,从而不更新索引,严重影响流量。

通常解决方案

  • 开发逻辑:用于未登录跳转、临时活动页引流;确保后端代码(如 Spring 的 redirect: 或 Flask 的 redirect())正确使用 302。
  • SEO 检查:使用爬虫工具定期检查,避免将永久性的 URL 规范化(如 HTTP->HTTPS)配置成了 302。
  • 缓存注意:302 通常不会被浏览器长期缓存,适合频繁变动的临时场景。

HTTP 303 See Other

HTTP 303 状态码的含义是 See Other(参见其他)。它的核心指令非常具体:要求客户端使用 GET 方法去另一个 URL 获取资源,无论你之前使用的是什么方法(哪怕是 POST 或 PUT)。

303 的存在主要是为了解决 POST 请求后的页面刷新问题。设想一个场景:你提交了一个 POST 表单(比如付款),服务器处理成功后,如果直接返回 200,用户刷新页面时,浏览器会再次弹出“确认重新提交表单”的警告,可能导致重复扣款。为了解决这个问题,服务器应该返回 303。它告诉浏览器:“POST 请求我处理完了,现在请你用 GET 方法去访问这个‘结果页面’的 URL。” 这样,用户刷新结果页面时,只是刷新 GET 请求,不会重复提交 POST 数据。

在语义上,303 明确表示“处理结果在别处”。它与 302 的主要区别在于强制转换方法:302 理论上允许保持原请求方法(虽然浏览器常转为 GET),而 303 则明确规定必须转为 GET。在现代 Web 开发中,303 常用于 RESTful API 中,当异步任务提交成功后,引导客户端去查询任务状态页(GET)。

通常解决方案

  • 防止表单重刷:在后端处理完 POST 请求后,使用 303 重定向到结果页,确保刷新安全。
  • API 设计:对于非资源创建的提交(如执行一个动作),成功后返回 303 指向状态查询接口。
  • 避免混淆:不要将 303 用于永久移动(用 301)或临时跳转(用 302),它专用于“处理完毕后的 GET 跳转”。

HTTP 304 Not Modified

HTTP 304 状态码的含义是 Not Modified(未修改)。它并不是严格意义上的“错误”,也不是常规的“成功响应”,而是服务器发给客户端的一个缓存验证信号

当你首次访问一个网页时,浏览器会下载 HTML、CSS、图片等资源,并在本地保存一份(缓存)。当你再次访问该页面时,浏览器不想白白浪费流量重新下载同样的文件,于是它会向服务器发送一个 条件请求。这通常包含两个头信息之一:

  1. If-Modified-Since:带上第一次下载时服务器返回的 Last-Modified 时间。
  2. If-None-Match:带上第一次下载时服务器返回的 ETag(实体标签,类似于文件的指纹)。

服务器收到请求后,会对比文件当前的版本。如果发现文件自上次访问以来没有任何变化,服务器就不会返回文件内容,而是直接发送一个 304 状态码。浏览器收到 304 后,心领神会,直接使用本地缓存的副本来渲染页面。

这种机制极大地节省了带宽加快了页面加载速度。对于用户来说,这意味着第二次打开网站会快很多;对于服务器来说,这意味着减少了数据传输的压力。

通常解决方案

  • 缓存配置:在 Nginx/Apache 中配置静态资源(图片、CSS、JS)的缓存策略,设置合理的 Cache-ControlExpires 头。
  • 开发调试:若修改了 CSS/JS 但浏览器不更新,通常是 304 缓存导致。使用 Ctrl+F5(强制刷新)或勾选 DevTools 中的“Disable cache”。
  • API 设计:对于不常变动的数据接口,也可以实现 ETag 机制,返回 304 以减少数据传输。

HTTP 305 Use Proxy

HTTP 305 状态码的含义是 Use Proxy(使用代理)。它曾经用于表示请求者必须通过指定的代理才能访问目标资源。服务器在返回 305 时,会在 Location 头中指明代理服务器的地址。

然而,这个状态码在现代互联网中几乎绝迹,且已被 RFC 7231 标准正式废弃。它被废弃的主要原因有两个:

  1. 安全风险:强制客户端使用特定代理,容易被滥用于“中间人攻击”或流量劫持,将用户引向恶意服务器。
  2. 客户端支持差:绝大多数现代浏览器出于安全考虑,直接不支持自动处理 305 响应,甚至会在控制台报错。

在早期的局域网设计中,它曾被设想用于强制内部员工通过特定的网关代理上网。但在今天,如果需要代理访问,通常由系统网络设置或 VPN 接管,而非依靠 HTTP 协议层的 305 指令。

通常解决方案

  • 服务器配置严禁使用 305。如需重定向,应使用 307 或 308;如需配置代理,请在服务器或操作系统层面设置,而非通过 HTTP 状态码。
  • 客户端排查:如果在抓包时罕见地遇到 305,通常是网络环境被劫持(如某些地区的网络认证页),尝试更换网络或检查代理设置。
  • 替代方案:现代架构中,负载均衡和反向代理(如 Nginx)是透明的,客户端无需感知代理的存在。

HTTP 306 Switch Proxy

HTTP 306 状态码的含义是 Switch Proxy(切换代理)。与 305 类似,这也是一个与代理服务器相关的状态码,但它不仅被现代互联网彻底抛弃,甚至连具体的应用场景都成了一笔“糊涂账”。

在 HTTP 协议的早期草案(1996 年)中,306 曾被设想为一种机制:当客户端当前使用的代理服务器需要更换时,服务器可以通过 306 状态码通知客户端切换到新的代理。提案中还配套了一个非标准的 Set-Proxy 头部来指定新代理的地址和有效期。

然而,这个想法最终未能转正,主要原因与 305 类似:

  1. 严重的安全隐患:允许服务器远程指挥客户端更改代理设置,无异于为“中间人攻击”敞开了大门。恶意服务器可以借此将用户的流量劫持到攻击者控制的代理上。
  2. 缺乏实际需求:在真实的网络环境中,代理的配置通常由系统管理员、VPN 软件或用户手动完成,完全不需要通过 HTTP 响应码来动态切换。

因此,在随后的 HTTP/1.1 标准(RFC 2616)及后续的所有版本中,306 被正式标记为“不再使用”(Unused),仅作为保留码存在于 IANA 注册表中

通常解决方案

  • 绝对禁用永远不要在生产环境中使用 306 状态码。没有任何现代浏览器或 HTTP 客户端支持它,发送此状态码只会让客户端感到困惑。
  • 历史冷知识:如果你在翻阅古老的 HTTP 协议草案或一些底层网络库的源代码(如某些语言中将 306 定义为 HttpStatusCode.Unused)时看到了它,知道它是一段被遗弃的历史即可。
  • 替代方案:如果业务确实需要动态切换代理,应通过专用的代理自动配置脚本(PAC)或系统级的网络管理工具来实现,而不是依赖 HTTP 状态码。

HTTP 307 Temporary Redirect

HTTP 307 状态码的含义是 Temporary Redirect(临时重定向)。它是 HTTP/1.1 标准中为了修复 302 的历史缺陷而引入的。

在 302 的早期实现中,很多浏览器会把 POST 请求重定向后的方法偷偷改成 GET。这在很多场景下(如支付、提交订单)是灾难性的。为了解决这个“偷懒”行为,RFC 标准定义了 307

307 的核心铁律是:禁止改变请求方法。 如果客户端是用 POST 请求的,重定向后必须还是 POST;如果是 PUT,重定向后必须还是 PUT。

你可以这样理解:

  • 302(临时搬家):老板让你去隔壁办公室找人,你可能顺便把走路变成了跑步(改变了方法)。
  • 307(临时绕行):老板严厉地说:“你现在的姿势(POST)不能变,保持这个姿势走到隔壁去!”

在实际应用中,307 常用于维护窗口服务降级。例如,当主服务器正在重启时,负载均衡器临时将所有请求(包括复杂的 POST 提交)转发到备用服务器,并确保数据提交的方式完全一致。此外,在 HTTPS 严格传输安全(HSTS) 配置中,浏览器也会使用 307 将 HTTP 强制转换为 HTTPS,以确保 POST 数据不会明文泄露。

通常解决方案

  • 服务器配置:在 Nginx 中使用 return 307 $new_url;。对于需要保持 POST 数据的临时跳转,首选 307 而非 302。
  • API 设计:如果你的接口接收非 GET 请求,且需要临时切换地址,务必使用 307 以保证请求体和方法不被篡改。
  • 前端调试:遇到 307 时,检查浏览器 Network 面板,确认 Request Method 在跳转前后是否一致。

HTTP 400 Bad Request

HTTP 400 状态码的含义是 Bad Request(错误请求)。它代表服务器无法理解或处理客户端发送的请求,通常是因为请求本身存在语法错误、格式无效或参数异常。

可以把 400 想象成你去邮局寄信,却把地址写在了信封里面,或者贴了失效的邮票。邮递员(服务器)看不懂你的意图,或者发现格式不对,就会把信退回来,并告诉你“请求无效”。

在 Web 开发中,400 是一个非常宽泛的客户端错误兜底码。常见触发原因包括:

  1. 参数错误:URL 参数缺失、数据类型不匹配(如给数字字段传了字符串)。
  2. 格式错误:请求体(Body)的 JSON 或 XML 格式不正确(如少了个括号)。
  3. 请求过大:请求体超过了服务器配置的 client_max_body_size 限制。
  4. 语义错误:请求的逻辑不合理(例如下单数量为负数)。

401(未授权)403(禁止) 不同,400 通常不涉及权限问题,纯粹是请求质量的问题。正确使用 400 可以帮助前端开发者快速定位 Bug,告知用户“不是服务器坏了,是你的输入有问题”。

通常解决方案

  • 前端校验:加强表单验证(必填项、格式正则),在请求发出前拦截明显错误,减少 400 请求。
  • 后端防御:对所有入参进行严格的 Schema 校验(如使用 Joi、Zod 库),一旦校验失败立即返回 400 并附带清晰的错误信息(如 { "error": "Email format invalid" })。
  • 日志排查:遇到 400 时,优先检查请求头(Headers)和请求体(Body)是否符合接口文档的定义。

HTTP 401 Unauthorized

HTTP 401 状态码的含义是 Unauthorized(未授权)。尽管英文直译为“未经授权”,但在实际语义中,它更准确地表达的是 “身份认证失败”“身份未知”

当服务器返回 401 时,它是在告诉客户端:“我不知道你是谁,请先出示证件(Credentials)。” 这通常发生在用户尝试访问受保护的资源,但未提供凭证,或提供的凭证(如 Token、Session ID)已过期、无效时。

技术上,401 响应必须包含一个 WWW-Authenticate 响应头,用于告知客户端应该使用何种方式进行认证(例如 Basic Auth、Bearer Token 等)。最常见的应用场景是 API 接口调用:如果请求头中没有携带有效的 Authorization: Bearer <token>,服务器就会返回 401。

区分 401403 是关键:

  • 401(未认证):你没带门禁卡,保安不让你进大楼。
  • 403(未授权):你带了门禁卡进了大楼,但你想进机房,保安说“你级别不够,不准进”。

通常解决方案

  • 客户端:检查本地存储的 Token 是否过期;确保在请求头中正确设置了 Authorization 字段;若过期则触发静默刷新或跳转登录页。
  • 服务端:返回 401 时,确保响应头包含 WWW-Authenticate;清理无效的 Cookie 或 Session,防止脏数据残留。
  • 用户体验:前端捕获 401 错误后,应避免无限弹窗,而是引导用户重新登录或刷新令牌。

HTTP 402 Payment Required

HTTP 402 状态码的含义是 Payment Required(需要付款)。在 RFC 标准定义中,它被归类为客户端错误响应,旨在作为一种预留机制,用于告知客户端“访问该资源需要支付费用”。

然而,尽管名字听起来很直接,402 实际上是一个 “实验性”且“非标准” 的状态码。自 HTTP/1.1 制定以来,它从未被 IETF(互联网工程任务组)正式推荐广泛使用,也没有形成统一的行业规范。原因在于早期的互联网协议设计并未预料到电子商务会如此发达,导致支付逻辑的复杂性远超一个简单的状态码能承载的范围。

尽管如此,在现实世界中,许多商业 API 和 SaaS 平台(如 Stripe、Twilio 等)会借用 402 来表示“配额耗尽”或“余额不足”。例如,当你的账户余额不足以发送短信或调用 AI 接口时,服务器可能会返回 402,提示你需要充值后才能继续使用该服务。这虽然不是官方标准,但已成为开发者社区的一种“默契”。

通常解决方案

  • 客户端处理:捕获 402 状态码,提示用户“余额不足”或“订阅已过期”,并引导其前往充值或升级套餐页面。
  • 服务端设计:谨慎使用 402。如果业务涉及付费,建议配合详细的 JSON 错误信息(如 { "error": "quota_exceeded", "code": 402 }),避免仅返回裸状态码。
  • 替代方案:现代 RESTful API 更倾向于使用 403 Forbidden429 Too Many Requests 来表达类似的业务限制,因为这两个状态码的标准化程度更高,兼容性更好。

HTTP 403 Forbidden

HTTP 403 状态码的含义是 Forbidden(禁止访问)。它表示服务器已经理解了请求,但拒绝执行它。与 401 不同,服务器明确指出:即使你提供了身份证明,你也没有权限访问这个资源

理解 401 和 403 的区别至关重要:

  • 401 Unauthorized(未认证):服务器不认识你。就像进小区没带门禁卡,保安拦住你让你登记。
  • 403 Forbidden(未授权):服务器认识你,但不想理你。就像你拿着门禁卡进了小区,却试图闯入别人的私人住宅,业主有权直接报警把你赶出去。

403 的常见触发场景包括:

  1. 权限不足:用户已登录,但不是管理员,试图访问后台管理接口。
  2. IP 黑名单:服务器配置了防火墙规则,屏蔽了你的 IP 地址。
  3. 文件权限:在 Linux 服务器上,Web 进程(如 www-data)没有读取某个文件(如 .htaccess)的权限。
  4. 目录浏览禁用:尝试访问一个没有默认首页(index.html)且禁止列目录的文件夹。

在开发 API 时,返回 403 必须非常谨慎,因为它暗示了服务器上存在该资源,只是你不配访问。有时出于安全考虑(防止黑客探测路径),开发者会选择返回 404 来掩盖真实情况。

通常解决方案

  • 前端处理:捕获 403 后,提示“权限不足”,并引导用户申请权限或切换账号,不要反复重试。
  • 后端/运维:检查服务器文件权限(chmod/chown);检查 Nginx/Apache 配置文件中的 allow/deny 规则或 WAF(防火墙)设置。
  • 安全策略:对于敏感接口,区分返回 401(未登录)和 403(已登录但无权),便于前端做差异化路由跳转。

HTTP 404 Not Found

HTTP 404 状态码的含义是 Not Found(未找到)。它是互联网上仅次于 200 的常见状态码,也是普通用户最熟悉的一个“错误提示”。它的核心语义非常简单:服务器找不到你请求的资源

当你在浏览器输入一个 URL 时,DNS 负责找到服务器(找到了大楼),服务器也接收到了请求,但在它的文件系统或数据库中,没有对应路径的文件或数据。这就好比你按地址找到了办公楼(服务器),但楼里根本没有你要找的那个房间号(资源)。

404 的产生原因多种多样:

  1. URL 拼写错误:最常见的原因,用户手抖输错了地址。
  2. 资源被删除:文章被作者删除了,或者商品下架了,链接却还留在那里。
  3. 链接老化:网站改版,URL 结构发生变化,旧的外部链接没有做重定向。

RESTful API 设计中,404 的使用非常讲究。它通常用于表示“ID 不存在”。例如 GET /users/999,如果数据库里没有 ID 为 999 的用户,就应该返回 404。但要注意区分 403 Forbidden:如果你返回 404,等于告诉攻击者“这个 ID 没有”;如果你返回 403,等于承认“这个 ID 存在,但你没权看”。为了安全,有时即使是存在的资源,如果没有权限,也会返回 404 来混淆视听。

通常解决方案

  • 前端处理:设计友好的 404 页面(引导用户回到首页或搜索),避免直接抛出冷冰冰的代码。
  • 后端/运维:配置 Nginx/Apache 的自定义 404 页面;对于失效的旧链接,尽量设置 301 重定向到新地址,避免流量流失。
  • SEO 优化:定期使用爬虫工具扫描死链(Broken Links),过多的 404 页面会降低搜索引擎对网站的评

HTTP 405 Method Not Allowed

HTTP 405 状态码的含义是 Method Not Allowed(方法禁用)。它表示服务器识别了请求的资源,但拒绝了该请求所使用的 HTTP 方法

简单来说,就是“地址是对的,但动作不对”。想象一下,你走进一家快餐店(服务器),对着柜台(端点 /order)掏出一张银行卡(POST 方法)要付钱,这是正常的。但如果你对着菜单(端点 /menu)试图用 POST 方法去“创建”一份菜单,服务员就会拒绝你,返回 405,并告诉你:“先生,菜单只能看(GET),不能改。”

RESTful API 设计中,405 极其常见。例如,一个用于查询用户信息的接口 GET /users/1 是允许的,但如果你用 DELETE /users/1 去删除用户,而该接口并未开放删除权限,服务器就应该返回 405。

根据 RFC 标准,服务器在返回 405 时,必须在响应头中包含 Allow 字段,列出该资源支持的 HTTP 方法(例如 Allow: GET, HEAD, OPTIONS)。这为客户端提供了明确的纠错指引,告诉开发者:“你只能用 GET 或 HEAD 访问这里,别瞎试 POST 或 DELETE。”

通常解决方案

  • 前端/客户端:检查请求方法是否正确(例如误将 GET 写成 POST);查看响应头中的 Allow 字段,确认服务端支持的操作。
  • 后端/API 设计:确保路由配置正确,例如只允许 GET 的接口不要意外支持 POST;对于不支持的方法,显式返回 405 而非 404。
  • 服务器配置:在 Nginx 中,可以通过 limit_except 指令限制特定方法的访问(如 limit_except GET POST { deny all; })。

HTTP 406 Not Acceptable

HTTP 406 状态码的含义是 Not Acceptable(不可接受)。这是一个在内容协商(Content Negotiation)过程中出现的错误,表示服务器存在对应的资源,但无法根据客户端的要求返回合适的表现形式

简单来说,就是“我有你想要的东西,但不是你要的样子”。

当客户端发起请求时,通常会在请求头中使用 Accept 字段来声明它能看懂的格式。例如:

  • Accept: application/json(我只吃 JSON 格式)
  • Accept: text/html(我要网页)

如果服务器只能提供 XML 格式,而客户端坚持只接受 JSON,服务器无法强行塞给对方一个 XML(因为对方可能解析不了),于是它会返回 406,礼貌地拒绝请求,并通常在响应体中列出自己支持哪些格式(例如:Available variants: text/html, application/xml)。

这种机制确保了数据的兼容性。虽然在现代前后端分离开发中,大家默认都使用 JSON,406 很少见,但在复杂的企业级集成多端适配(如同时支持 Web 端 HTML 和 App 端 JSON)场景中,它依然发挥着“守门员”的作用,防止客户端拿到无法解析的数据。

通常解决方案

  • 客户端检查:核对请求头中的 Accept 字段是否拼写错误,或是否过于苛刻(例如要求 q=1.0 的优先级过高)。
  • 服务端配置:确保服务器(如 Nginx 或后端框架)配置了正确的 MIME 类型映射,能够输出客户端需要的格式。
  • 调试技巧:使用 Postman 或 cURL 去掉 Accept 头试试,如果 200 成功,说明问题确实出在内容协商的格式不匹配上。

HTTP 407 Proxy Authentication Required

HTTP 407 状态码的含义是 Proxy Authentication Required(需要代理认证)。它是 401 Unauthorized 的“代理版”,专门用于代理服务器场景。

当你通过代理服务器访问互联网时,流程是这样的:你 -> 代理服务器 -> 目标网站。如果代理服务器设置了访问门槛(比如公司内网要求验证身份),它就会向你返回 407。这就像你开车上了高速,到了收费站(代理服务器),收费员拦住你说:“请出示通行卡(代理凭证)。”

与 401 不同,407 的认证发生在代理层,而非最终的 Web 服务器层。客户端必须在请求头中携带 Proxy-Authorization(注意不是 Authorization)来提供凭据,通常是 Base64 编码的用户名和密码。

常见触发场景包括:

  1. 企业内网:公司网络要求员工配置代理认证才能上外网。
  2. 付费代理池:爬虫使用的付费代理服务,需要在请求头中携带 Proxy-Authorization 才能使用。
  3. 透明代理:某些网络环境下的防火墙或网关。

通常解决方案

  • 客户端配置:检查系统或浏览器的代理设置(Proxy Settings),填入正确的用户名和密码;若是代码请求,需在 Header 中添加 Proxy-Authorization: Basic <Base64编码的凭证>
  • 爬虫开发:若使用代理 IP 遇到 407,说明代理需要认证或已失效,需更换代理或补充鉴权信息。
  • 服务器排查:如果是自建代理(如 Squid、Nginx 反向代理),检查 proxy_pass 及相关认证模块的配置文件。

HTTP 408 Request Timeout

HTTP 408 状态码的含义是 Request Timeout(请求超时)。它表示服务器在等待请求的过程中,客户端迟迟没有发送完整的数据,服务器失去了耐心,单方面关闭了连接

这种情况通常发生在慢连接大文件上传的场景中。HTTP 协议允许服务器设置一个“超时时间”(Keep-Alive Timeout)。如果在规定时间内(例如 60 秒),客户端没有把请求头或请求体发送完毕,服务器就会返回 408 并断开连接,以释放资源给其他用户。

一个典型的例子是网络极差环境下的表单提交。用户点击提交,但由于网络抖动,数据包发送缓慢,服务器等了半天也没收到完整的请求体,于是返回 408 告诉客户端:“我等累了,你自己重发吧。”

需要注意的是,408 与 504 Gateway Timeout 不同:

  • 408:是你(客户端)连服务器的时候太慢,服务器不等你了。
  • 504:是你(客户端)连服务器很快,但服务器去连上游数据库或第三方 API 时,上游反应太慢,服务器等不及了。

在开发 API 时,如果客户端频繁收到 408,通常意味着网络链路不稳定,或者客户端上传的数据量过大且没有做好分片。

通常解决方案

  • 客户端:检查网络连接稳定性;对于大文件上传,实现断点续传或分片上传,避免单次请求耗时过长。
  • 服务端配置:在 Nginx 中调整 client_body_timeoutclient_header_timeout 参数,适当延长等待时间。
  • 用户体验:前端捕获 408 错误后,提示“网络超时,请检查网络后重试”,并自动尝试重新发送请求。

HTTP 409 Conflict

HTTP 409 状态码的含义是 Conflict(冲突)。它表示服务器在尝试处理请求时,检测到请求与服务器当前的资源状态发生了冲突,因此无法完成请求。

与 400(通用错误)不同,409 强调的是逻辑上的互斥。最经典的例子是并发编辑冲突。假设你正在编辑一个在线文档的第 5 版,在你提交修改的同时,另一个人也提交了修改,服务器已经更新到了第 6 版。此时你的请求(基于第 5 版)就会导致版本冲突,服务器返回 409,告诉你:“抱歉,你操作的版本已过时,请刷新页面后再试。”

另一个常见场景是唯一性约束。例如,在用户注册接口中,如果你尝试注册一个已经被占用的邮箱(email 字段设置了 Unique Key),服务器就应该返回 409 Conflict,而不是 400 Bad Request。因为你的请求格式(JSON)是正确的,只是内容(邮箱)与现有数据冲突了。

在 RESTful API 设计中,409 常用于乐观锁(Optimistic Locking)机制。通过在请求头中携带 If-MatchIf-Unmodified-Since,客户端可以确保自己操作的是最新的资源版本,一旦冲突,服务器立即拒绝。

通常解决方案

  • 前端处理:捕获 409 后,提示用户“数据已被他人修改,请刷新页面重试”,并重新拉取最新数据覆盖当前表单。
  • 后端设计:在实现 PUT/PATCH 更新时,使用版本号(Version)或时间戳(Timestamp)校验,防止脏写(Dirty Write)。
  • 数据库层:利用数据库的原子操作(如 INSERT ... ON CONFLICT DO NOTHING)来处理唯一键冲突,并映射到 409 状态码。

HTTP 410 Gone

HTTP 410 状态码的含义是 Gone(已消失)。它比 404 更进一步,明确告诉客户端:“你要找的这个资源,以前确实存在,但现在已经被永久删除了,而且我也不知道它去哪了,以后也不会再有了。”

如果说 404 是“查无此人”,410 就是“此人已故”。这种明确性对于搜索引擎优化(SEO)至关重要。当爬虫遇到 404 时,它会犹豫:是网站暂时挂了?还是链接写错了?它可能会稍后再来尝试抓取几次。但当爬虫遇到 410 时,它会立刻明白这是一个“死链接”,并迅速将该页面从索引库中彻底移除,从而帮助网站清理垃圾索引,维持健康的 SEO 状态。

在实际开发中,410 常用于以下场景:

  1. 内容清理:网站改版,删除了大量过时的文章或产品页,且不打算做重定向。
  2. 用户注销:用户注销账号后,其个人主页返回 410,表示该用户已永久离开。
  3. API 生命周期管理:某个旧版本的 API(如 /v1/*)被废弃且下线,返回 410 比 404 更能告知开发者“该升级了”。

通常解决方案

  • SEO 优化:对于确定不再恢复的死链,配置服务器返回 410,加速搜索引擎清理,避免浪费爬虫预算。
  • 服务器配置:在 Nginx 中使用 return 410; 针对特定路径(如废弃的目录)进行配置。
  • 前端提示:设计专门的 410 页面,告知用户“该内容已被永久移除”,而非通用的 404 “找不到页面”。

HTTP 411 Length Required

HTTP 411 状态码的含义是 Length Required(需要长度)。这是一个非常具体的客户端错误,表示服务器拒绝接受没有包含 Content-Length 头的请求

在 HTTP 协议中,Content-Length 头用于告诉服务器:“我这次寄给你的包裹(请求体)有多重(多少字节)。” 有些服务器或网关(尤其是处理文件上传或流式数据时)为了确保稳定性,会强制要求客户端在发送数据前先报个数。如果客户端直接发送数据而不声明长度,服务器为了防止资源耗尽或被恶意攻击(如 Slowloris 攻击),会直接返回 411 并断开连接。

这就好比你去寄快递,快递员要求你必须称重并填写重量,如果你抱着一个大箱子说“我不知道多重,你直接寄吧”,快递员就会拒绝收件(返回 411)。

在现代 RESTful API 开发中,虽然 Transfer-Encoding: chunked(分块传输)越来越普遍,不需要提前声明总长度,但在某些老旧的企业级防火墙特定的硬件负载均衡器严格的 Web 服务中,依然会强制要求 Content-Length

通常解决方案

  • 客户端:在发送 POST 或 PUT 请求时,确保 HTTP 库自动计算了 Content-Length;如果是手写 Socket 请求,务必手动添加该头信息。
  • 服务端:检查 Nginx 或 Apache 配置,确认是否开启了 client_max_body_size 或相关的严格模式;若非必要,可放宽限制以支持分块传输。
  • 调试:使用 cURL 或 Postman 测试时,若遇到 411,尝试手动添加 -H "Content-Length: xxx" 头来验证问题。

HTTP 412 Precondition Failed

HTTP 412 状态码的含义是 Precondition Failed(前置条件失败)。它是一种用于并发控制缓存一致性的机制,确保客户端只在“特定条件成立”的情况下才能操作资源。

客户端在发送请求时,可以带上 条件请求头(如 If-MatchIf-Unmodified-Since)。服务器在处理请求前,会先检查这些条件是否与服务器当前状态匹配。如果不匹配,服务器就返回 412,并拒绝执行请求。

最常见的应用场景是防止并发修改(乐观锁)。假设你要编辑一篇维基百科文章:

  1. 你先 GET 文章,服务器返回 ETag: "abc123"
  2. 你修改后 PUT 提交,并在请求头中带上 If-Match: "abc123",意思是:“只有在这篇文章还是 abc123 版本时,才允许我保存。”
  3. 如果在此期间别人抢先修改了文章,服务器的 ETag 变成了 def456,你的请求条件就不匹配了,服务器返回 412,告诉你:“抱歉,数据已被他人改动,请刷新后再编辑。”

通常解决方案

  • 前端处理:捕获 412 错误,提示用户“数据已过期,正在刷新...”,然后重新拉取最新数据并覆盖当前表单。
  • 后端实现:在更新(PUT/PATCH)接口中,务必校验 If-Match(ETag)或 If-Unmodified-Since(时间戳),防止脏写。
  • 调试技巧:检查请求头中的条件值是否正确;如果是 GET 请求遇到 412,可能是缓存策略配置错误。

HTTP 413 Payload Too Large

HTTP 413 状态码的含义是 Payload Too Large(负载过大)。它以前被称为 Request Entity Too Large,表示服务器拒绝处理请求,因为请求体(Request Body)的大小超过了服务器设定的上限

这就好比你去寄快递,快递公司规定单件包裹不能超过 20kg,你扛了一个 50kg 的大箱子过去,快递员二话不说直接拒收,并指着墙上的规定告诉你超重了。

在 Web 开发中,413 最常出现在文件上传场景。例如,Nginx 默认的 client_max_body_size 是 1MB,如果你尝试上传一个 10MB 的视频,Nginx 就会直接拦截请求并返回 413,请求甚至都到不了你的后端应用程序。

区分 413414(URI Too Long)很重要:

  • 413:是 Body 太大(如上传的文件、提交的 JSON 数据)。
  • 414:是 URL 太长(如 GET 请求的参数拼接得太长)。

通常解决方案

  • 前端处理

    • 在用户选择文件时就进行大小校验,提前提示“文件不能超过 10MB”,避免无用请求。
    • 对于超大文件(如视频),实现分片上传(Chunk Upload),将一个大文件切成多个小块,每块单独发送,避开服务器的单次大小限制。
  • 后端/运维配置

    • Nginx:在 nginx.conf 或站点配置中调整 client_max_body_size 100m;(设置为 100MB)。
    • Apache:调整 LimitRequestBody
    • Node.js (Express):设置 app.use(express.json({ limit: '50mb' }))
  • API 设计:对于允许大文件上传的接口,务必在文档中明确标注最大限制,并返回清晰的错误信息(如 { "error": "File size exceeds 10MB limit" })。

HTTP 414 URI Too Long

HTTP 414 状态码的含义是 URI Too Long(请求地址过长)。它表示服务器拒绝为请求提供服务,因为请求的 URI(通常是 URL)长度超过了服务器能够解析的上限

这与 413 Payload Too Large 完全不同:

  • 413:是身体(请求体/文件)太胖,服务器抱不动。
  • 414:是名字(URL 地址)太长,服务器记不住。

在早期 Web 开发中,414 常见于极端情况下的 GET 请求。例如,有人试图把一个巨大的表单(几百个字段)通过 URL 参数传递(如 ?filter=a&filter=b&...),导致 URL 长达几千个字符。现代浏览器通常支持很长的 URL,但服务器(如 Nginx、Apache)和防火墙通常有严格的硬编码限制,一旦超过就会直接掐断连接,返回 414。

此外,这也可能是安全攻击的信号。攻击者有时会构造超长的 URL 来尝试绕过某些简陋的 Web 应用防火墙(WAF)或导致服务器缓冲区溢出。

通常解决方案

  • 前端重构

    • 改用 POST:如果你在用 GET 传递大量参数(如复杂的筛选条件、JSON 字符串),请改为 POST 请求,将数据放在 Request Body 中。
    • 缩短参数:使用哈希值或 ID 代替冗长的文本参数。
  • 服务器配置

    • Nginx:调整 large_client_header_buffers 指令(默认是 4k 或 8k)。如果必须支持长 URL,可以增加这个值(如 large_client_header_buffers 4 16k;),但通常不建议过度放宽,以免遭受攻击。
  • API 设计

    • 避免设计需要通过 URL 传递复杂数组或对象的接口。如果必须传,请使用 POST + JSON Body。

HTTP 415 Unsupported Media Type

HTTP 415 状态码的含义是 Unsupported Media Type(不支持的媒体类型)。它表示服务器拒绝接受请求,因为请求体的格式(Media Type)是服务器无法处理的

关键在于请求头中的 Content-Type。当你向服务器发送数据(如 POST 或 PUT)时,你必须告诉服务器:“我发给你的这些数据是什么格式的(是 JSON、XML 还是纯文本)?” 如果你发送的是 JSON 数据,但忘记设置 Content-Type: application/json,或者设置成了 application/xml,服务器就会返回 415,表示:“我看不懂你发的这个格式,我不处理。”

它与 406 Not Acceptable 经常被混淆,区别如下:

  • 415(请求错误):我看不懂你发给我的是什么格式。(关于 Request Body)
  • 406(接受错误):我看不懂你要我返回的什么格式。(关于 Response Body / Accept 头)

在 RESTful API 开发中,415 是最常见的错误之一。例如,后端接口只接受 JSON,但前端错误地使用了 FormData 格式提交,就会触发 415。

通常解决方案

  • 前端检查

    • 确认 Content-Type 请求头设置正确。对于 JSON API,必须是 Content-Type: application/json
    • 确认请求体(Body)的格式与 Content-Type 声明的一致(例如,声明是 JSON,Body 就必须是合法的 JSON 字符串,不能是对象)。
  • 后端/服务器配置

    • Nginx:确保没有通过 proxy_set_headerdefault_type 错误地覆盖了 Content-Type。
    • 框架设置:在 Express/Koa/Spring 中,确保已注册了解析对应类型的中间件(如 express.json()@Consumes 注解)。
  • 调试技巧:使用 Postman 或 cURL 手动发送请求,明确指定 -H "Content-Type: application/json",看是否解决问题。

HTTP 416 Range Not Satisfiable

HTTP 416 状态码的含义是 Range Not Satisfiable(范围请求无法满足)。它是断点续传分片下载场景中的专用错误码,表示客户端请求的文件字节范围(Byte Range)是无效的

当你下载一个大文件时,下载工具会发送 Range 头告诉服务器:“我要第 1000 到 2000 字节的数据。” 如果服务器发现这个文件总共只有 500 字节,或者你请求的范围起始点大于文件大小,服务器就会返回 416,意思是:“你要的那一段数据根本不存在。”

常见触发原因包括:

  1. 文件已变更:下载过程中,服务器上的文件被更新或替换了(文件大小变了),导致之前计算的分片位置失效。
  2. 错误的范围计算:客户端代码 Bug,计算的分片起始位置(Start Byte)超过了文件总长度。
  3. 末尾越界:请求 Range: bytes=1000- 时,文件实际只有 800 字节。

通常解决方案

  • 客户端处理

    • 遇到 416 时,立即停止当前分片下载,重新获取文件头信息(HEAD 请求),确认最新的 Content-Length,然后重新计算分片。
    • 在视频播放器中,如果拖拽进度条出现 416,通常意味着视频源已更新,需要重新加载视频。
  • 服务端/API 设计

    • 确保 Content-Range 响应头返回正确的范围格式(如 bytes */1234,其中 1234 是文件总大小)。
    • 对于动态生成的文件,如果无法支持 Range 请求,应直接返回 200 并发送完整文件,而不是返回 416。
  • 调试技巧

    • 使用 cURL 测试:curl -I -H "Range: bytes=1000-" http://example.com/file.zip,检查返回的 Content-Length 是否小于 1000。

HTTP 417 Expectation Failed

HTTP 417 状态码的含义是 Expectation Failed(期望失败)。这是一个相对罕见的错误,源于 HTTP 协议中的 “Expect” 请求头机制

在 HTTP/1.1 中,客户端可以在发送正式请求体(Body)之前,先通过 Expect 头询问服务器:“我准备给你发一大堆数据,你准备好接收了吗?” 最常见的用法是 Expect: 100-continue

这个机制的工作流程是:

  1. 客户端发送请求头,包含 Expect: 100-continue
  2. 服务器检查请求头(如权限、大小限制),如果一切正常,返回 100 Continue
  3. 客户端收到 100 后,才开始发送庞大的请求体(如大文件)。

如果服务器不支持这种期望,或者拒绝该请求(例如认为请求体太大或不合法),它就会返回 417,告诉客户端:“别发了,我不接受你的预检条件。”

通常解决方案

  • 客户端(最常见)

    • 移除 Expect 头:大多数现代 HTTP 库(如 Axios, Fetch API, OkHttp)默认不再发送 Expect: 100-continue,或者在遇到 417 时会自动降级重试。如果你手写请求,请确保没有多余添加此头。
    • 调整超时:有时 417 是因为服务器响应慢,客户端等不到 100 状态码就超时了,尝试增加超时时间。
  • 服务端/代理配置

    • Nginx:如果遇到 417,检查是否启用了某些安全模块或 Lua 脚本拦截了请求。可以尝试在配置中关闭对 Expect 头的检查(虽然 Nginx 默认支持 100-continue)。
    • 后端框架:确保应用服务器(如 Tomcat, Node.js)正确处理了 Expect 头。
  • API 设计:除非你在处理极大型的文件上传且需要精细的流量控制,否则不建议在 API 设计中依赖 Expect 机制,因为它会增加握手延迟,且在复杂的代理链中容易出错。

HTTP 418 I'm a teapot

HTTP 418 状态码的含义是 I'm a teapot(我是茶壶)。它不是为了解决实际的网络技术问题,而是互联网历史上最著名的一个愚人节玩笑(RFC 2324)

在 1998 年 4 月 1 日发布的 HTCPCP(超文本咖啡壶控制协议) 中,定义了这个状态码。它的设定是:当客户端试图用茶壶来煮咖啡时,茶壶应该返回 418,拒绝这个荒谬的请求。

虽然这是一个玩笑,但它在程序员文化中占据了神圣的地位,象征着“拒绝执行不可能完成的任务”或“请求过于愚蠢”。

在现实世界中,418 并没有官方地位,但很多开发者喜欢用它来:

  1. 嘲讽错误请求:当收到明显不合理或恶意的参数时,返回 418 作为幽默的拒绝。
  2. 占位符:在开发初期,用来标记尚未实现的功能(“我是个茶壶,不会煮咖啡”)。
  3. 彩蛋:Google 的搜索彩蛋(搜索 "askew" 会让页面倾斜)就是这种精神的延续。

有趣的事实:2017 年,谷歌的 Go 语言团队曾试图从源码中移除 418 的支持,理由是“它不是标准协议”。结果引发了全球程序员的强烈抗议(“Save 418”),最终 Go 团队撤回了提议,保留了这一传统。

通常解决方案

  • 遇到 418 怎么办:如果你在调用某个 API 时意外收到 418,首先检查你的请求参数是否逻辑不通(比如试图让一个只读接口执行写入操作)。
  • 开发调试:这通常是你自己写的 Mock 服务或中间件返回的,检查一下代码中是否有 res.status(418).send('I am a teapot')
  • 生产环境千万不要在生产环境的严肃业务中使用 418,除非你想让你的用户一脸懵逼。

HTTP 451 Unavailable For Legal Reasons

HTTP 451 状态码的含义是 Unavailable For Legal Reasons(因法律原因不可用)。这是一个相对较新的状态码(2016 年正式纳入 RFC 7725),专门用于表示由于法律限制(如版权、审查、隐私法规),服务器无法提供该资源

403 Forbidden 不同,403 是服务器主动拒绝(我不想给你看),而 451 是服务器被迫拒绝(法律不让我给你看)。

这个号码的灵感来源于雷·布雷伯里的反乌托邦小说《华氏 451 度》(Fahrenheit 451),书中消防员的工作是焚烧书籍以禁止知识传播。在互联网语境下,451 成为了网络审查合规限制的数字标志。

常见触发场景包括:

  1. 版权侵权(DMCA):网站收到了数字千年版权法案(DMCA)的下架通知,被迫移除某部电影或音乐。
  2. 政府审查:某些国家/地区根据当地法律屏蔽了特定政治或社会内容。
  3. 隐私保护(GDPR):根据欧盟《通用数据保护条例》,如果数据处理不当,可能需要限制访问包含个人数据的页面。
  4. 诽谤与司法禁令:法院下令要求网站删除某篇诽谤文章。

通常解决方案

  • 服务端/运维

    • 返回清晰理由:在响应体中详细说明封锁的法律依据(例如:“根据 DMCA 第 X 条,该内容已被移除”)。
    • 提供申诉链接:如果适用,提供联系邮箱或申诉入口,方便用户或版权所有者沟通。
    • Nginx 配置return 451; 并在页面中展示法律声明。
  • 前端/用户

    • 如果遇到 451,通常无法通过技术手段解决(如换浏览器、清缓存无效)。
    • 如果是 VPN 用户,尝试切换到未被限制的节点(视当地法律而定)。
    • 如果是内容创作者,检查是否收到了版权投诉。

HTTP 500 Internal Server Error

HTTP 500 状态码的含义是 Internal Server Error(内部服务器错误)。它是服务器端错误的“老大哥”,也是我们在上网时最常遇到的让人抓狂的报错。

简单来说,当你访问一个网站时,服务器在处理你的请求时突然遇到了一个意料之外的错误,导致它无法完成请求,但又不知道该如何具体向你解释,于是它就抛出一个通用的 500 错误

与 4xx 错误(客户端的问题)不同,500 错误纯粹是服务器端的锅。这通常意味着服务器上的代码有 Bug、配置有误或者依赖的服务挂了。

常见触发原因包括:

  1. 代码异常:后端代码出现了未捕获的异常(比如 Java 的空指针异常、Python 的语法错误或除零错误)。
  2. 数据库问题:数据库连接失败、死锁,或者执行的 SQL 语句有语法错误。
  3. 资源耗尽:服务器的内存、CPU 或磁盘空间被耗尽,导致无法分配资源给新的请求。
  4. 文件权限错误:Web 服务器(如 Nginx、Apache)没有足够的权限去读取或写入某个文件。
  5. 第三方服务故障:你的应用依赖的外部 API 挂了,导致你的服务器在等待响应时超时或崩溃。

通常解决方案

  • 普通用户侧

    • 刷新页面:有时候可能只是服务器瞬间高并发导致的偶发错误,稍后重试可能就好了。
    • 清除缓存和 Cookie:某些情况下,浏览器存储的过期或损坏的 Cookie 会导致服务器处理出错。
    • 联系网站管理员:如果一直出现 500 错误,说明对方网站确实出了技术故障,只能等待对方修复。
  • 开发者/运维侧(如何排查)

    • 第一时间查日志:这是最重要的!查看应用本身的错误日志(如 Java 的 Log4j、Node.js 的 console.error)和 Web 服务器错误日志(如 Nginx 的 error.log),通常里面会有详细的报错堆栈信息。
    • 检查近期变更:是不是刚刚上线了新代码或改了配置文件?尝试回滚到上一个稳定版本。
    • 监控服务器资源:检查 CPU、内存和磁盘使用情况,看看是否资源耗尽。
    • 测试数据库连接:确认数据库服务是否正常运行,连接池是否溢出。

HTTP 501 Not Implemented

HTTP 501 状态码的含义是 Not Implemented(未实现)。它表示服务器不具备完成请求所需的功能,或者不支持请求的方法

与 500(服务器崩了)不同,501 是一种明确的“能力不足”声明。就好比你走进一家只卖早餐的包子铺(服务器),非要人家给你做满汉全席(请求),老板会直接告诉你:“对不起,我们做不了这个(501)。”

常见触发场景包括:

  1. 不支持的 HTTP 方法:服务器只实现了 GET 和 POST,但你发送了一个 COPYPATCH 请求。
  2. 协议版本过低:客户端使用了服务器无法理解的 HTTP 协议版本(例如非常老旧的设备尝试使用 HTTP/2 特性)。
  3. 功能阉割:某些轻量级服务器(如嵌入式设备中的 Web Server)为了简化代码,故意不实现某些高级功能。

501 vs 405

  • 405 Method Not Allowed:服务器认识这个方法(比如知道 POST 是啥),但这个特定资源不允许用 POST。
  • 501 Not Implemented:服务器根本不认识这个方法(比如从来没听过 BREW 这个方法),或者整个服务器都没实现该功能。

通常解决方案

  • 客户端排查

    • 检查请求方法是否拼写错误(如把 POST 写成 POSt)。
    • 确认请求的 API 端点是否真的支持该方法(查阅 API 文档)。
  • 服务端/运维

    • 代码补全:如果是自研服务,确认后端路由是否实现了对应的 Controller 方法。
    • 服务器升级:如果是老旧服务器(如很老的 Nginx/Apache),尝试升级版本以支持新的 HTTP 方法或协议。
    • 反向代理:确保 Nginx 等代理没有拦截掉某些特殊的 HTTP 方法(如 WebDAV 的 PROPFIND)。
  • API 设计:在 RESTful 设计中,如果某个资源确实不支持 DELETE,应返回 405 而非 501,因为 DELETE 是标准方法,只是该资源不支持。

HTTP 502 Bad Gateway

HTTP 502 状态码的含义是 Bad Gateway(错误网关)。它是一个网关或代理服务器特有的错误,表示作为“中间人”的服务器,从上游服务器收到了一条无效的响应

在互联网架构中,很少有服务器是“裸奔”的。通常是这样的链路:

用户浏览器 -> Nginx (反向代理) -> 应用服务器 (Node.js/Java/Python) -> 数据库

在这个链条中,Nginx 就是“网关”。当用户访问网站时,Nginx 需要把请求转交给背后的应用服务器处理。如果应用服务器崩溃了进程闪退端口关闭,Nginx 连不上它,或者收到了乱七八糟的无法解析的响应,Nginx 就会无奈地向用户返回 502

核心区别:

  • 500 Internal Server Error:应用服务器活着,但代码写崩了(比如代码里有除以零的错误)。
  • 502 Bad Gateway:应用服务器死了,连响应都给不出来,或者给了一堆乱码。

通常解决方案

  • 检查上游服务状态

    • 登录服务器,检查你的应用是否还在运行。例如:ps aux | grep nodesystemctl status nginx
    • 尝试重启应用服务:sudo systemctl restart your_app_service
  • 检查端口与配置

    • 确认 Nginx 配置中的 proxy_pass 指向的端口号是否正确(例如是否把 3000 写成了 3001)。
    • 检查应用是否监听在 0.0.0.0 而不是 127.0.0.1(后者可能导致 Docker 容器间无法互通)。
  • 查看日志(最关键)

    • Nginx 错误日志tail -f /var/log/nginx/error.log。这里通常会明确写着 connect() failed (111: Connection refused) while connecting to upstream,直接告诉你后端连不上。
    • 应用日志:查看你的后端应用有没有启动时报错的日志。
  • 资源耗尽

    • 检查服务器内存是否爆满(OOM),导致系统自动杀死了应用进程。

HTTP 503 Service Unavailable

HTTP 503 状态码的含义是 Service Unavailable(服务不可用)。它表示服务器目前无法处理请求,但这通常是暂时的,通常是由于服务器过载正在停机维护

与 500(内部错误)不同,503 往往是一种“主动的拒绝”。服务器知道自己还活着,但暂时没有能力或不想处理你的请求。就像你去餐厅吃饭,门口挂着“今日盘点,暂停营业”的牌子(503),而不是厨房着火了(500)。

常见触发场景包括:

  1. 服务器维护:系统正在更新代码、重启服务或打补丁。运维人员会特意配置 503,防止用户访问半成品。
  2. 流量过载:双十一抢购或突发流量导致服务器 CPU 飙升至 100%,为了保命,服务器开始丢弃新来的请求。
  3. DDoS 防护:防火墙检测到异常流量,暂时屏蔽了你的 IP 或所有请求。
  4. 依赖故障:微服务架构中,核心依赖(如数据库、第三方支付接口)挂了,导致上层服务无法工作。

专业提示:正规的 503 响应应该包含 Retry-After 头,告诉客户端多久之后再来尝试(例如 Retry-After: 3600 表示一小时后重试)。

通常解决方案

  • 客户端/用户

    • 稍等片刻再刷新页面。
    • 如果是 App,提示“服务器繁忙,请稍后再试”。
  • 运维/开发

    • 优雅停机:在发布新版本时,先从负载均衡中摘掉服务器(摘除流量),更新完毕后再加回去,避免用户看到 503。
    • 限流(Rate Limiting):使用 Nginx 或 Redis 限制单个 IP 的请求频率,防止恶意刷爆服务器。
    • 负载均衡:增加服务器实例,分担压力。
    • 检查依赖:确认数据库、Redis 或外部 API 是否存活。

HTTP 504 Gateway Timeout

HTTP 504 状态码的含义是 Gateway Timeout(网关超时)。它表示作为网关或代理的服务器,在等待上游服务器(Upstream Server)响应时超时了

回顾一下服务器链路:

用户 -> Nginx (网关) -> 应用服务器 -> 数据库/第三方API

如果应用服务器处理请求的时间太长(比如执行一个超级复杂的 SQL 查询,或者调用的第三方支付接口卡住了),超过了 Nginx 设定的等待时间,Nginx 就会放弃等待,并向用户返回 504

核心区别(502 vs 504):

  • 502 Bad Gateway:上游服务器挂了,连响应都没有(连接被拒绝)。
  • 504 Gateway Timeout:上游服务器活着,但干活太慢,网关等得不耐烦了。

通常解决方案

  • 排查上游性能

    • 数据库:检查是否有慢查询(Slow Query)。很多时候 504 是因为 SQL 没有走索引,导致全表扫描耗时过长。
    • 代码逻辑:检查是否有死循环,或者同步调用了耗时的外部接口(如 AI 绘图、大文件处理)。
  • 调整超时配置(Nginx)

    • 增加网关等待时间。在 nginx.conf 或站点配置中调整:
proxy_connect_timeout 60s; # 连接上游服务器的超时
proxy_read_timeout 300s;  # 等待上游服务器响应的超时
proxy_send_timeout 300s;
  • 架构优化

    • 异步化:如果任务确实很耗时(如生成报表),不要让用户一直转圈等待。改为“提交任务 -> 立即返回 202 Accepted -> 前端轮询任务状态”的模式。
    • 熔断机制:如果第三方 API 经常超时,引入熔断器(如 Hystrix 或 Resilience4j),直接快速失败,避免线程堆积。
  • 网络连通性:检查网关与上游服务器之间的网络是否通畅,是否存在丢包。

HTTP 505 HTTP Version Not Supported

HTTP 505 状态码的含义是 HTTP Version Not Supported(不支持的 HTTP 版本)。它表示服务器拒绝支持客户端请求中使用的 HTTP 协议版本

可以把 HTTP 协议版本想象成交流的语言。客户端说:“我用 HTTP/3 跟你说话。” 如果服务器太老,或者配置太保守,只懂 HTTP/1.1,听不懂 HTTP/3,它就会返回 505,意思是:“我不懂你在说什么鸟语,换个我能听懂的版本再跟我说。”

在现实中,505 非常罕见,因为现代服务器和客户端(浏览器)通常都有很好的向下兼容机制。但在以下极端或特殊场景中可能出现:

  1. 老旧服务器:一个运行着十几年前代码的嵌入式设备服务器,只支持 HTTP/1.0,而你强行发送 HTTP/2 请求。
  2. 配置错误:服务器配置中错误地禁用了某些协议版本(例如在 Nginx 中错误设置了 ssl_protocols)。
  3. 安全策略:某些防火墙或 WAF(Web 应用防火墙)为了安全,故意屏蔽了某些被认为不安全的旧协议版本(如 TLS 1.0),导致客户端连接失败。

通常解决方案

  • 客户端排查

    • 检查代码中是否强制指定了某种 HTTP 版本(例如某些 HTTP 库允许设置 http_version='1.0')。
    • 尝试降低协议版本重试(例如从 HTTP/2 降级到 HTTP/1.1)。
  • 服务端/运维配置

    • Nginx:检查 listen 指令和 SSL 配置。确保支持主流协议:

      nginx
      nginxserver {
          listen 443 ssl http2; # 启用 HTTP/2
          ssl_protocols TLSv1.2 TLSv1.3; # 确保支持现代 TLS 协议
          ...
      }
    • Apache:检查 Protocols 配置。
  • 网络环境:如果你在公司内网或特殊网络环境下遇到 505,可能是中间的网络代理设备(Proxy)限制了协议版本。

觉得内容不错?我要

打赏杯咖啡或蜜雪冰城吧
微信扫一扫
微信赞赏码
支付宝扫一扫
支付宝赞赏码
评论 暂无评论
暂无评论,快来抢沙发吧~