401 和 403 到底怎么选?
401 Unauthorized 的语义其实是「未认证」:没有提供凭证,或凭证无效,客户端应当去登录。403 Forbidden 是「已认证但无权访问」:身份已知,但该身份不允许访问这个资源,再登录也没用。名字里的 Unauthorized 是历史遗留的误导。
1xx–5xx 状态码与 MIME 类型、Content-Type 速查
| 状态码 | 名称 | 含义 | 复制 |
|---|---|---|---|
| 100 | Continue | 客户端应继续发送请求体 | |
| 101 | Switching Protocols | 服务器同意切换协议,如升级到 WebSocket | |
| 102 | Processing | 服务器已收到请求但尚未完成处理(WebDAV) | |
| 103 | Early Hints | 在最终响应前提前返回部分响应头,用于预加载资源 | |
| 200 | OK | 请求成功,响应体包含结果 | |
| 201 | Created | 资源创建成功,通常用于 POST 后提示:建议在 Location 头返回新资源地址 | |
| 202 | Accepted | 请求已接受但尚未处理完成,适合异步任务 | |
| 204 | No Content | 处理成功但不返回响应体提示:常用于 DELETE、PUT;响应不能带 body | |
| 206 | Partial Content | 返回部分内容,用于断点续传与视频拖动提示:需配合 Range / Content-Range 头 | |
| 301 | Moved Permanently | 资源永久转移到新地址提示:浏览器与搜索引擎会缓存,慎重使用 | |
| 302 | Found | 资源临时转移,后续仍用原地址提示:历史实现可能把 POST 改成 GET | |
| 303 | See Other | 用 GET 访问另一个地址查看结果提示:PRG 模式(POST-Redirect-GET)常用 | |
| 304 | Not Modified | 资源未变化,直接用本地缓存提示:配合 ETag / If-None-Match 协商缓存 | |
| 307 | Temporary Redirect | 临时重定向且保持原请求方法 | |
| 308 | Permanent Redirect | 永久重定向且保持原请求方法 | |
| 400 | Bad Request | 请求语法错误或参数不合法 | |
| 401 | Unauthorized | 未认证,需要先登录提示:语义上是「未认证」,不是「无权限」 | |
| 402 | Payment Required | 需要付费(保留状态码) | |
| 403 | Forbidden | 已认证但无权访问该资源提示:不要在 403 里暴露资源是否存在 | |
| 404 | Not Found | 请求的资源不存在 | |
| 405 | Method Not Allowed | 该资源不支持此 HTTP 方法提示:应返回 Allow 头列出支持的方法 | |
| 406 | Not Acceptable | 无法满足 Accept 头要求的响应格式 | |
| 408 | Request Timeout | 服务器等待请求超时 | |
| 409 | Conflict | 请求与资源当前状态冲突提示:并发更新冲突、唯一键重复常用 | |
| 410 | Gone | 资源曾存在但已被永久删除提示:比 404 更明确,利于搜索引擎移除索引 | |
| 411 | Length Required | 缺少 Content-Length 头 | |
| 412 | Precondition Failed | 请求头中的前置条件不成立 | |
| 413 | Content Too Large | 请求体超过服务器允许的大小提示:文件上传报错常见原因 | |
| 414 | URI Too Long | 请求的 URL 过长 | |
| 415 | Unsupported Media Type | 请求体的内容类型不被支持提示:接口要求 JSON 但发了表单时常见 | |
| 416 | Range Not Satisfiable | Range 头指定的范围无法满足 | |
| 418 | I'm a teapot | 愚人节彩蛋状态码(HTCPCP/1.0) | |
| 422 | Unprocessable Content | 语法正确但语义校验失败提示:表单字段校验失败常用 | |
| 425 | Too Early | 服务器不愿处理可能被重放的请求 | |
| 428 | Precondition Required | 要求请求带上条件头以避免并发覆盖 | |
| 429 | Too Many Requests | 请求过于频繁,被限流提示:通常配合 Retry-After 头 | |
| 431 | Request Header Fields Too Large | 请求头过大提示:Cookie 过多时会触发 | |
| 500 | Internal Server Error | 服务器内部错误提示:不要把堆栈信息返回给客户端 | |
| 501 | Not Implemented | 服务器不支持该请求方法 | |
| 502 | Bad Gateway | 网关或代理从上游收到无效响应提示:后端进程崩溃时 Nginx 常见 | |
| 503 | Service Unavailable | 服务暂时不可用,通常是过载或维护提示:应配合 Retry-After | |
| 504 | Gateway Timeout | 网关等待上游响应超时 | |
| 505 | HTTP Version Not Supported | 不支持请求使用的 HTTP 版本 | |
| 507 | Insufficient Storage | 服务器存储空间不足(WebDAV) | |
| 508 | Loop Detected | 检测到无限循环(WebDAV) |
参考状态码语义以 RFC 9110 为准;MIME 类型以 IANA 注册表为准。搜索支持中文与英文关键词。
HTTP 状态码分五类:1xx 信息、2xx 成功、3xx 重定向、4xx 客户端错误、5xx 服务端错误。其中几组最容易混淆:401 表示「未认证」(需要登录),403 表示「已认证但无权限」;301 是永久重定向会被浏览器与搜索引擎缓存,302 是临时的;502 是网关收到了上游的无效响应,504 是网关等待上游超时。
MIME 类型(Media Type)通过 Content-Type 头告诉客户端该如何处理响应体。写错类型会带来很具体的后果:把 JSON 接口写成 text/html 会让前端解析失败,把下载文件写成可识别的类型会让浏览器直接打开而不是下载。本页同时给出常见场景该用哪个 Content-Type 的对照。
401 Unauthorized 的语义其实是「未认证」:没有提供凭证,或凭证无效,客户端应当去登录。403 Forbidden 是「已认证但无权访问」:身份已知,但该身份不允许访问这个资源,再登录也没用。名字里的 Unauthorized 是历史遗留的误导。
301 会被浏览器长期缓存,搜索引擎也会把权重转移到新地址,一旦设错很难撤回(用户浏览器里已缓存)。临时跳转、A/B 测试、灰度切流都应该用 302 或 307。只有确定永久迁移时才用 301,且要避免形成重定向环。
两者都是网关(Nginx 等)发出的:502 表示网关连上了上游但收到了无效响应,通常是后端进程崩溃或端口没起来;504 表示网关等待上游响应超时,通常是后端处理太慢。先看后端进程是否存活,再看慢查询或下游依赖的超时设置。