什么是状态码
HTTP 状态码是服务器返回给客户端的三位数字代码,用于表示请求的处理结果。状态码分为 5 大类,每类都有特定的含义。
状态码分类
1xx - 信息性状态码(Informational)
2xx - 成功状态码(Success)
3xx - 重定向状态码(Redirection)
4xx - 客户端错误状态码(Client Error)
5xx - 服务器错误状态码(Server Error)
1xx 信息性状态码
这类状态码表示请求已被接收,继续处理。
100 Continue
含义:服务器已收到请求头,客户端应继续发送请求主体。
使用场景:
- 客户端发送大文件前,先发送请求头
- 服务器检查请求头后,决定是否接收主体
示例:
# 客户端请求
POST /upload HTTP/1.1
Host: example.com
Content-Length: 1048576
Expect: 100-continue
# 服务器响应
HTTP/1.1 100 Continue
# 客户端继续发送主体
[文件数据...]
101 Switching Protocols
含义:服务器同意切换协议。
使用场景:
- WebSocket 握手
- HTTP/2 升级
示例:
# 客户端请求
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
# 服务器响应
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
2xx 成功状态码
这类状态码表示请求已成功被服务器接收、理解并处理。
200 OK
含义:请求成功。
使用场景:
- GET 请求成功获取资源
- POST 请求成功处理
- PUT/PATCH 请求成功更新
示例:
HTTP/1.1 200 OK
Content-Type: application/json
{"id":1,"name":"Zhang San"}
201 Created
含义:请求成功并创建了新资源。
使用场景:
- POST 请求创建新资源
- PUT 请求创建新资源
示例:
HTTP/1.1 201 Created
Location: /api/users/123
Content-Type: application/json
{"id":123,"name":"Zhang San","email":"zhangsan@example.com"}
202 Accepted
含义:请求已接受,但尚未处理完成。
使用场景:
- 异步处理任务
- 批量操作
示例:
HTTP/1.1 202 Accepted
Content-Type: application/json
{"message":"Request accepted","task_id":"abc123"}
204 No Content
含义:请求成功,但没有返回内容。
使用场景:
- DELETE 请求成功
- PUT 请求成功但不需要返回数据
- 表单提交成功
示例:
HTTP/1.1 204 No Content
206 Partial Content
含义:服务器成功处理了部分 GET 请求。
使用场景:
- 断点续传
- 视频流播放
- 大文件下载
示例:
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1023/2048
Content-Length: 1024
[部分内容...]
3xx 重定向状态码
这类状态码表示需要客户端采取进一步的操作才能完成请求。
301 Moved Permanently
含义:资源已永久移动到新位置。
特点:
- 浏览器会缓存新地址
- 搜索引擎会更新索引
示例:
HTTP/1.1 301 Moved Permanently
Location: <https://www.example.com/new-page>
使用场景:
- 网站域名更换
- URL 结构调整
- HTTP 升级到 HTTPS
302 Found
含义:资源临时移动到新位置。
特点:
- 浏览器不会缓存
- 搜索引擎不会更新索引
示例:
HTTP/1.1 302 Found
Location: /temporary-page
使用场景:
- 临时维护页面
- A/B 测试
- 临时跳转
303 See Other
含义:使用 GET 方法访问另一个 URL。
使用场景:
- POST 请求后重定向到结果页面
- 防止表单重复提交
示例:
HTTP/1.1 303 See Other
Location: /success-page
304 Not Modified
含义:资源未修改,可以使用缓存。
使用场景:
- 条件 GET 请求
- 浏览器缓存验证
示例:
# 客户端请求
GET /image.jpg HTTP/1.1
If-Modified-Since: Mon, 27 Jul 2024 12:00:00 GMT
# 服务器响应
HTTP/1.1 304 Not Modified
307 Temporary Redirect
含义:临时重定向,保持原请求方法。
与 302 的区别:
- 307 保证不改变请求方法
- 302 可能将 POST 改为 GET
示例:
HTTP/1.1 307 Temporary Redirect
Location: /new-location
308 Permanent Redirect
含义:永久重定向,保持原请求方法。
与 301 的区别:
- 308 保证不改变请求方法
- 301 可能将 POST 改为 GET
示例:
HTTP/1.1 308 Permanent Redirect
Location: /new-permanent-location
4xx 客户端错误状态码
这类状态码表示客户端的请求有错误。
400 Bad Request
含义:请求语法错误,服务器无法理解。
常见原因:
- JSON 格式错误
- 缺少必需参数
- 参数类型错误
示例:
GET 请求的 URL 中包含非法字符(如未转义的空格、中文)
curl http://example.com/api?name=test user
HTTP/1.1 400 Bad Request
Content-Type: application/json
{"error":"Invalid JSON format"}
401 Unauthorized
含义:需要身份认证。
特点:
- 需要提供认证信息
- 通常返回 WWW-Authenticate 头部
示例:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Access to API"
Content-Type: application/json
{"error":"Authentication required"}
403 Forbidden
含义:服务器理解请求,但拒绝执行。
与 401 的区别:
- 401:未认证
- 403:已认证但无权限
示例:
HTTP/1.1 403 Forbidden
Content-Type: application/json
{"error":"You don't have permission to access this resource"}
404 Not Found
含义:请求的资源不存在。
使用场景:
- URL 路径错误
- 资源已被删除
- API 端点不存在
示例:
HTTP/1.1 404 Not Found
Content-Type: application/json
{"error":"Resource not found"}
405 Method Not Allowed
含义:请求方法不被允许。
特点:
- 返回 Allow 头部,列出允许的方法
示例:
HTTP/1.1 405 Method Not Allowed
Allow: GET, POST
Content-Type: application/json
{"error":"DELETE method is not allowed"}
408 Request Timeout
含义:服务器等待请求超时。
使用场景:
- 客户端发送请求过慢
- 网络连接不稳定
示例:
HTTP/1.1 408 Request Timeout
Content-Type: application/json
{"error":"Request timeout"}
409 Conflict
含义:请求与服务器当前状态冲突。
使用场景:
- 资源版本冲突
- 重复创建资源
- 并发修改冲突
示例:
HTTP/1.1 409 Conflict
Content-Type: application/json
{"error":"User with this email already exists"}
413 Payload Too Large
含义:请求主体过大。
使用场景:
- 上传文件过大
- POST 数据过多
示例:
HTTP/1.1 413 Payload Too Large
Content-Type: application/json
{"error":"File size exceeds 10MB limit"}
415 Unsupported Media Type
含义:不支持的媒体类型。
使用场景:
- Content-Type 不正确
- 服务器不支持该格式(比如你请求的content type是json,但是服务器支持的是text/html)
示例:
HTTP/1.1 415 Unsupported Media Type
Content-Type: application/json
{"error":"Content-Type must be application/json"}
429 Too Many Requests
含义:请求过多,触发限流。
使用场景:
- API 限流
- 防止滥用
示例:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json
{"error":"Rate limit exceeded. Try again in 60 seconds"}
5xx 服务器错误状态码
这类状态码表示服务器在处理请求时发生错误。
500 Internal Server Error
含义:服务器内部错误。
常见原因:
- 代码异常
- 数据库错误
- 配置错误
示例:
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
{"error":"Internal server error"}
501 Not Implemented
含义:服务器不支持该功能。
使用场景:
- 请求的功能未实现
- 不支持的 HTTP 方法
示例:
HTTP/1.1 501 Not Implemented
Content-Type: application/json
{"error":"This feature is not implemented"}
502 Bad Gateway
含义:网关或代理服务器收到无效响应。
使用场景:
- 上游服务器故障
- 代理配置错误
示例:
HTTP/1.1 502 Bad Gateway
Content-Type: application/json
{"error":"Bad gateway"}
503 Service Unavailable
含义:服务暂时不可用。
常见原因:
- 服务器维护
- 服务器过载
- 临时故障
示例:
HTTP/1.1 503 Service Unavailable
Retry-After: 3600
Content-Type: application/json
{"error":"Service temporarily unavailable"}
504 Gateway Timeout
含义:网关或代理服务器超时。
使用场景:
- 上游服务器响应慢
- 网络延迟
示例:
HTTP/1.1 504 Gateway Timeout
Content-Type: application/json
{"error":"Gateway timeout"}
常用状态码速查表
| 状态码 | 含义 | 常见场景 |
|---|---|---|
| 200 | OK | 请求成功 |
| 201 | Created | 资源创建成功 |
| 204 | No Content | 删除成功 |
| 301 | Moved Permanently | 永久重定向 |
| 302 | Found | 临时重定向 |
| 304 | Not Modified | 使用缓存 |
| 400 | Bad Request | 请求格式错误 |
| 401 | Unauthorized | 需要认证 |
| 403 | Forbidden | 无权限 |
| 404 | Not Found | 资源不存在 |
| 405 | Method Not Allowed | 方法不允许 |
| 429 | Too Many Requests | 请求过多 |
| 500 | Internal Server Error | 服务器错误 |
| 502 | Bad Gateway | 网关错误 |
| 503 | Service Unavailable | 服务不可用 |
状态码选择指南
成功响应
- 200:通用成功响应
- 201:创建资源成功
- 204:成功但无内容返回
重定向
- 301:永久重定向(SEO 友好)
- 302:临时重定向
- 304:资源未修改(缓存)
客户端错误
- 400:请求格式错误
- 401:未认证
- 403:已认证但无权限
- 404:资源不存在
- 409:资源冲突
服务器错误
- 500:通用服务器错误
- 503:服务暂时不可用
状态码的安全意义
理解状态码不仅有助于调试和 API 设计,更重要的是它们与 Web 安全密切相关。
1. 认证和授权相关状态码的安全实践
401 Unauthorized 的正确使用
安全问题:不当的 401 响应可能泄露敏感信息。
错误示例:
HTTP/1.1 401 Unauthorized{ "error": "用户名 'admin' 存在,但密码错误"}
问题:攻击者可以通过这个响应枚举有效的用户名。
正确示例:
HTTP/1.1 401 Unauthorized{ "error": "用户名或密码错误"}
最佳实践:
- 不要在错误消息中区分”用户名不存在”和”密码错误”
- 登录失败时返回统一的错误消息
- 实现登录尝试限制,防止暴力破解
- 使用相同的响应时间,避免时序攻击
完整的安全登录响应:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
WWW-Authenticate: Bearer realm="api"
X-RateLimit-Remaining: 2
{
"error": "认证失败",
"message": "用户名或密码错误",
"retry_after": 3
}
403 Forbidden 的安全考虑
403 vs 404 的选择:
场景:用户尝试访问其他用户的私有资源
选项 1:返回 403 Forbidden
GET /api/users/123/private-data
HTTP/1.1 403 Forbidden
问题:确认了资源存在,可能泄露信息
选项 2:返回 404 Not Found
GET /api/users/123/private-data
HTTP/1.1 404 Not Found
优点:隐藏资源的存在性,更安全
安全原则:
- 对于敏感资源:返回 404 而不是 403
示例:用户尝试访问不属于自己的订单 返回 404,让攻击者无法确认订单是否存在 - 对于公开资源:可以返回 403
示例:未登录用户访问需要登录的页面 返回 403,明确告知需要认证 - 记录可疑的 403 请求:
if (response.status === 403) { logger.warn('Unauthorized access attempt', { user: req.user.id, resource: req.path, ip: req.ip, timestamp: new Date() });}
2. 重定向状态码的安全风险
开放重定向漏洞
什么是开放重定向?
攻击者利用重定向功能将用户引导到恶意网站。
漏洞示例:
// 危险代码app.get('/redirect', (req, res) => {
const url = req.query.url; res.redirect(302, url); // 直接使用用户输入});// 攻击 URLhttps://bank.com/redirect?url=https://evil.com/fake-login
攻击流程:
1. 攻击者发送钓鱼邮件:
"您的账户有异常,请点击链接验证:
<https://bank.com/redirect?url=https://evil.com/fake-login>"
2. 用户看到 bank.com 域名,信任并点击
3. bank.com 将用户重定向到 evil.com
4. evil.com 显示伪造的登录页面
5. 用户输入凭据,被攻击者窃取
安全防护:

最佳实践:
- 使用白名单:只允许重定向到可信域名
- 验证 URL 格式:确保是有效的 URL
- 使用相对路径:优先使用站内相对路径
- 记录重定向:记录所有重定向请求以便审计
301 vs 302 的安全影响
301 Moved Permanently(永久重定向):
影响:
- 浏览器会缓存重定向
- 搜索引擎会更新索引
- 可能导致旧 URL 永久失效
安全考虑:
- 确保新 URL 长期有效
- 避免将敏感 URL 永久重定向到不安全的位置
302 Found(临时重定向):
影响:
- 浏览器不会缓存
- 搜索引擎保留原 URL
- 适合临时性的重定向
安全考虑:
- 适合登录后的重定向
- 适合临时维护页面
3. 错误状态码的信息泄露风险
500 Internal Server Error 的安全处理
危险示例:

泄露的信息:
- 数据库类型(MySQL)
- 数据库用户名(root)
- 数据库密码(password)
- 服务器内部路径
- 技术栈信息
正确示例:

安全实践:

404 Not Found 的安全应用
用途 1:隐藏资源存在性

用途 2:防止路径遍历

4. 状态码与速率限制
429 Too Many Requests 的安全应用:

响应示例:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 900
X-RateLimit-Limit: 5
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640000000
{
"error": "请求过于频繁",
"message": "您已超过登录尝试次数限制",
"retry_after": 900
}
5. 缓存相关状态码的安全考虑
304 Not Modified 的安全使用
问题场景:敏感数据被缓存
# 第一次请求
GET /api/user/profile
HTTP/1.1 200 OK
ETag: "abc123"
Cache-Control: private, max-age=3600
{"name":"Zhang San","salary":50000}
# 第二次请求
GET /api/user/profile
If-None-Match: "abc123"
HTTP/1.1 304 Not Modified
# 浏览器使用缓存的数据
安全风险:
- 敏感数据被缓存在浏览器中
- 共享设备上可能被其他用户访问
- 缓存可能在用户登出后仍然存在
安全实践:

状态码安全检查清单
在设计和实现 API 时,请检查以下项目:
- [ ] 401 响应不泄露用户名是否存在
- [ ] 敏感资源返回 404 而不是 403
- [ ] 实现了开放重定向防护
- [ ] 500 错误不返回敏感的技术细节
- [ ] 实现了速率限制(429)
- [ ] 敏感数据不使用 304 缓存
- [ ] 错误响应包含唯一的错误 ID
- [ ] 记录所有认证失败(401)和授权失败(403)
- [ ] 为不同的错误场景返回合适的状态码
- [ ] 生产环境隐藏详细的错误堆栈
思考题
- 301 和 302 的主要区别是什么?
- 401 和 403 有什么不同?什么时候应该返回 404 而不是 403?
- 什么时候应该返回 204 状态码?
- 为什么需要 304 状态码?它有什么安全风险?
- 如果登录失败时返回”用户名不存在”或”密码错误”,会有什么安全问题?
- 为什么生产环境的 500 错误不应该返回详细的错误信息?
小结
- HTTP 状态码分为 5 大类(1xx-5xx),每类有特定含义
- 2xx 成功:请求被正确处理
- 3xx 重定向:需要客户端采取进一步操作,注意开放重定向风险
- 4xx 客户端错误:请求有误,注意信息泄露风险
- 5xx 服务器错误:服务器处理失败,不要返回敏感信息
- 正确使用状态码是 API 设计和安全的基础
- 401/403 响应要避免泄露敏感信息
- 敏感资源考虑返回 404 而不是 403
- 实现开放重定向防护
- 500 错误不返回技术细节
- 使用 429 实现速率限制
- 敏感数据避免使用缓存相关状态码