狀態碼寶典(下):4xx-5xx 客戶端與伺服器錯誤
上一篇我們學習了 1xx-3xx 狀態碼,本篇將完整解析 4xx(客戶端錯誤)和 5xx(伺服器錯誤)。這些狀態碼是開發和除錯過程中最常遇到的「老朋友」。
一、 4xx 客戶端錯誤
4xx 狀態碼表示「請求有問題,錯誤在客戶端」。伺服器無法處理請求,通常是因為請求格式錯誤、缺少認證或資源不存在。
400 Bad Request
含義:請求格式錯誤,伺服器無法理解
常見原因:
- JSON 格式錯誤(缺少引號、多餘逗號)
- 必填參數缺失
- 參數類型錯誤(需要數字但傳了字串)
- URL 格式錯誤
POST /api/users HTTP/1.1
Content-Type: application/json
{"name": "John", "age": "not a number"}
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": "Bad Request",
"message": "age must be a number",
"field": "age"
}TIP
回應 Body 應包含具體的錯誤原因,幫助開發者快速定位問題。
401 Unauthorized
含義:未提供認證憑證,或憑證無效
常見原因:
- 未攜帶 Token
- Token 已過期
- Token 格式錯誤
GET /api/users/me HTTP/1.1
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"
{
"error": "Unauthorized",
"message": "Missing or invalid access token"
}重點:
- 應包含
WWW-Authenticate標頭,告知認證方式 - 與 403 的區別:401 是「不知道你是誰」,403 是「知道你是誰,但你沒權限」
403 Forbidden
含義:伺服器理解請求,但拒絕執行
常見原因:
- 無權訪問該資源
- IP 被封鎖
- 帳號被停用
- 資源被設為私有
DELETE /api/users/456 HTTP/1.1
Authorization: Bearer valid_token
HTTP/1.1 403 Forbidden
{
"error": "Forbidden",
"message": "You don't have permission to delete this user"
}401 vs 403 比較:
| 狀態碼 | 含義 | 情境 |
|---|---|---|
| 401 | 未認證 | 沒登入、Token 過期 |
| 403 | 無權限 | 已登入,但沒有權限 |
404 Not Found
含義:請求的資源不存在
常見原因:
- URL 路徑錯誤
- 資源已被刪除
- ID 不存在
GET /api/users/99999 HTTP/1.1
HTTP/1.1 404 Not Found
{
"error": "Not Found",
"message": "User with id 99999 does not exist"
}NOTE
有時出於安全考慮,即使資源存在但無權訪問,也會回傳 404(避免洩漏資源存在的資訊)。
405 Method Not Allowed
含義:請求方法不被允許
DELETE /api/users HTTP/1.1
HTTP/1.1 405 Method Not Allowed
Allow: GET, POST
{
"error": "Method Not Allowed",
"message": "DELETE is not allowed on /api/users"
}重點:
- 必須包含
Allow標頭,列出允許的方法
406 Not Acceptable
含義:伺服器無法產生客戶端可接受的格式
GET /api/users HTTP/1.1
Accept: application/xml
HTTP/1.1 406 Not Acceptable
{
"error": "Not Acceptable",
"message": "This API only supports application/json"
}408 Request Timeout
含義:伺服器等待請求超時
HTTP/1.1 408 Request Timeout
{
"error": "Request Timeout",
"message": "The server did not receive a complete request in time"
}409 Conflict
含義:請求與伺服器當前狀態衝突
常見場景:
- 嘗試創建已存在的資源
- 併發修改衝突
- 違反唯一性約束
POST /api/users HTTP/1.1
Content-Type: application/json
{"email": "john@example.com", "name": "John"}
HTTP/1.1 409 Conflict
{
"error": "Conflict",
"message": "A user with this email already exists"
}410 Gone
含義:資源曾經存在,但已永久移除
與 404 的區別:
| 狀態碼 | 含義 | 快取 |
|---|---|---|
| 404 | 找不到(可能從未存在) | 不建議快取 |
| 410 | 已永久刪除(曾經存在) | 可以快取 |
GET /api/posts/123 HTTP/1.1
HTTP/1.1 410 Gone
{
"error": "Gone",
"message": "This post has been permanently deleted"
}413 Payload Too Large
含義:請求體超過伺服器限制
POST /api/upload HTTP/1.1
Content-Length: 104857600
HTTP/1.1 413 Payload Too Large
Retry-After: 3600
{
"error": "Payload Too Large",
"message": "File size exceeds the 10MB limit"
}414 URI Too Long
含義:URL 超過伺服器限制
GET /api/search?q=very...long...query HTTP/1.1
HTTP/1.1 414 URI Too LongTIP
使用 POST + Body 來傳遞大量查詢參數,而非全塞在 URL 中。
415 Unsupported Media Type
含義:不支援請求的 Content-Type
POST /api/users HTTP/1.1
Content-Type: application/xml
<user><name>John</name></user>
HTTP/1.1 415 Unsupported Media Type
{
"error": "Unsupported Media Type",
"message": "Only application/json is supported"
}422 Unprocessable Entity
含義:請求格式正確,但語意錯誤(驗證失敗)
與 400 的區別:
| 狀態碼 | 含義 | 場景 |
|---|---|---|
| 400 | 格式錯誤 | JSON 語法錯誤 |
| 422 | 驗證失敗 | 格式正確但內容不合法 |
POST /api/users HTTP/1.1
Content-Type: application/json
{"email": "not-an-email", "age": -5}
HTTP/1.1 422 Unprocessable Entity
{
"error": "Validation Error",
"details": [
{"field": "email", "message": "Invalid email format"},
{"field": "age", "message": "Age must be positive"}
]
}429 Too Many Requests
含義:請求頻率過高,觸發限流
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704700800
{
"error": "Too Many Requests",
"message": "Rate limit exceeded. Try again in 60 seconds"
}重點:
- 應包含
Retry-After標頭 - 可包含
X-RateLimit-*標頭提供更多資訊
二、 5xx 伺服器錯誤
5xx 狀態碼表示「伺服器發生錯誤」。問題在伺服器端,客戶端的請求可能是完全正確的。
500 Internal Server Error
含義:伺服器內部錯誤(萬用錯誤碼)
常見原因:
- 程式碼拋出未處理的異常
- 資料庫連線失敗
- 空指標引用
HTTP/1.1 500 Internal Server Error
{
"error": "Internal Server Error",
"message": "An unexpected error occurred",
"requestId": "abc-123-def"
}CAUTION
不要在 5xx 回應中暴露堆疊追蹤或敏感資訊!只應包含請求 ID 供日誌查詢。
501 Not Implemented
含義:伺服器不支援請求使用的功能
TRACE /api/users HTTP/1.1
HTTP/1.1 501 Not Implemented
{
"error": "Not Implemented",
"message": "TRACE method is not implemented"
}502 Bad Gateway
含義:作為網關或代理的伺服器,從上游收到無效回應
常見原因:
- 後端服務掛了
- 後端服務回應格式錯誤
- 網路問題
503 Service Unavailable
含義:伺服器暫時無法處理請求
常見原因:
- 維護中
- 過載
- 正在部署
HTTP/1.1 503 Service Unavailable
Retry-After: 300
{
"error": "Service Unavailable",
"message": "Server is under maintenance. Please try again in 5 minutes"
}重點:
- 應包含
Retry-After標頭 - 這是暫時的,與 500 的「可能需要修復」不同
504 Gateway Timeout
含義:網關或代理等待上游伺服器超時
與 408 的區別:
| 狀態碼 | 誰超時 |
|---|---|
| 408 | 伺服器等待客戶端超時 |
| 504 | 代理等待後端超時 |
HTTP/1.1 504 Gateway Timeout
{
"error": "Gateway Timeout",
"message": "Upstream server took too long to respond"
}三、 錯誤回應最佳實踐
3.1 統一錯誤格式
建議使用一致的錯誤回應結構:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "One or more fields are invalid",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Email format is invalid"
}
],
"requestId": "req-abc-123"
}
}3.2 錯誤分類與處理
3.3 指數退避重試
對於 5xx 錯誤,建議使用指數退避策略:
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options);
if (response.status >= 500) {
throw new Error(`Server error: ${response.status}`);
}
return response;
} catch (error) {
if (i === maxRetries - 1) throw error;
// 指數退避:1s, 2s, 4s...
const delay = Math.pow(2, i) * 1000;
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
}四、 實用速查表
4xx 系列
| 狀態碼 | 名稱 | 常見場景 |
|---|---|---|
| 400 | Bad Request | 參數錯誤、JSON 格式錯誤 |
| 401 | Unauthorized | 未登入、Token 無效 |
| 403 | Forbidden | 無權限 |
| 404 | Not Found | 資源不存在 |
| 405 | Method Not Allowed | 方法不支援 |
| 409 | Conflict | 資源衝突 |
| 410 | Gone | 資源已永久刪除 |
| 413 | Payload Too Large | 上傳檔案太大 |
| 415 | Unsupported Media Type | Content-Type 不支援 |
| 422 | Unprocessable Entity | 驗證失敗 |
| 429 | Too Many Requests | 觸發限流 |
5xx 系列
| 狀態碼 | 名稱 | 常見場景 |
|---|---|---|
| 500 | Internal Server Error | 伺服器內部錯誤 |
| 501 | Not Implemented | 功能未實作 |
| 502 | Bad Gateway | 後端服務無響應 |
| 503 | Service Unavailable | 維護中、過載 |
| 504 | Gateway Timeout | 後端超時 |
總結
| 類別 | 含義 | 客戶端行為 |
|---|---|---|
| 4xx | 客戶端錯誤 | 檢查請求、修正後重試 |
| 5xx | 伺服器錯誤 | 等待、重試,或聯繫支援 |
> **記憶口訣**:
- 4xx = 「你的錯」
- 5xx = 「我的錯」
選擇狀態碼的原則:
- 精確優先:選擇最能描述問題的狀態碼
- 安全考慮:必要時用 404 取代 403,避免洩漏資訊
- 一致性:整個 API 使用統一的錯誤格式
- 可操作性:提供足夠的資訊讓客戶端知道如何處理
進階挑戰
- 設計一個完整的錯誤回應格式,包含錯誤碼、訊息、欄位驗證錯誤、請求 ID。在什麼情況下應該包含哪些資訊?
- 思考:為什麼有些網站會在用戶登入失敗時回傳「帳號或密碼錯誤」而不是明確指出是哪個錯了?這算 400 還是 401?
- 實作一個帶有指數退避的 HTTP 客戶端,對 5xx 錯誤自動重試最多 3 次。