Khi bạn nhấn F5 để reload trang, một cuộc “đối thoại” vô hình đang diễn ra giữa trình duyệt và máy chủ. Cuộc đối thoại đó bắt đầu bằng yêu cầu (request) và kết thúc bằng mã trạng thái HTTP – ba chữ số nhỏ bé nhưng mang thông điệp lớn về kết quả của mọi tương tác web.
Như một developer, bạn không chỉ cần đọc hiểu các mã này, mà còn phải sử dụng đúng chúng. Một header 404 sai nơi có thể khiến Google nghĩ trang bạn đã mất, một 500 không được bắt sẽ làm hỏng trải nghiệm người dùng. Bài viết này là tổng hợp đầy đủ và thực tế nhất về HTTP status codes – từ nguyên lý đến ứng dụng.
Tại Sao Mã Trạng Thái HTTP Quan Trọng?
- SEO & Ranking: Googlebot đọc status code để hiểu trang của bạn có tồn tại hay không.
301đúng cách giữ nguyên PageRank,404khiến URL bị loại khỏi index. - UX & Accessibility: Người dùng blind với screen reader cần thông báo lỗi rõ ràng.
403 Forbiddenkhác với404 Not Found– một là “bạn không được phép”, hai là “không tìm thấy”. - API Design: RESTful API dựa trên status code để biểu thị kết quả.
201 Createdthay vì trả về{ success: true }– đó là convention, không phải choice. - Monitoring & Alerting: Log aggregation (ELK, Datadog) dựa trên status code để đếm lỗi, trigger alert. Tăng đột biến
5xx= server down.
Phân Loại Chi Tiết Từng Nhóm
HTTP status code gồm 5 chữ số: chữ số đầu tiên xác định lớp (class), hai chữ số sau xác định loại cụ thể. Dưới đây là danh sách đầy đủ nhất bạn cần biết.
1xx – Thông Báo (Informational)
Nhóm này hiếm gặp trong ứng dụng web thông thường. Chúng là “tín hiệu tạm thời” – máy chủ đã nhận request và đang xử lý, chưa trả về kết quả cuối.
| Mã | Tên | Ý nghĩa | Use case |
|---|---|---|---|
100 | Continue | Máy chủ đồng ý nhận request body | Upload file lớn, client gửi Expect: 100-continue header |
101 | Switching Protocols | Chuyển sang protocol khác (WebSocket upgrade) | WebSocket handshake: Upgrade: websocket |
102 | Processing | Server đang xử lý (WebDAV) | Phương thức PROPPATCH của WebDAV |
2xx – Thành Công (Success)
Nhóm mong muốn nhất. Request đã được xử lý thành công. Dưới đây là các mã bạn sẽ gặp hàng ngày.
| Mã | Tên | Ý nghĩa | Ví dụ thực tế |
|---|---|---|---|
200 | OK | Standard success response | GET /api/users trả về JSON array |
201 | Created | Resource được tạo mới | POST /api/users tạo user mới, trả về user object với ID |
202 | Accepted | Request đã chấp nhận nhưng chưa xử lý xong | POST /jobs để tạo background job, trả về job_id ngay |
204 | No Content | Thành công nhưng không có body | DELETE /api/users/123 – xóa xong, không cần trả về gì |
206 | Partial Content | Chỉ trả về một phần resource (Range request) | Video streaming: Range: bytes=0-1023 |
Best practice: Luôn trả về 201 Created với header Location: /resource/{id} khi tạo mới resource. Tránh dùng 200 OK cho POST tạo mới.
3xx – Chuyển Hướng (Redirection)
Client cần thực hiện thêm hành động. Đây là nhóm quan trọng cho SEO và caching.
| Mã | Tên | Ý nghĩa | Khi nào dùng |
|---|---|---|---|
301 | Moved Permanently | URL đã di chuyển vĩnh viễn | Thay đổi URL slug, chuyển HTTP → HTTPS, canonical redirect |
302 | Found | Temporary redirect (method có thể đổi) | A/B testing, maintenance page, login flow |
303 | See Other | Redirect bằng GET, dù request gốc là POST | Sau khi submit form, redirect về trang kết quả với GET |
304 | Not Modified | Khi gửi If-Modified-Since hoặc If-None-Match và resource chưa đổi | |
307 | Temporary Redirect | Như 302 nhưng giữ nguyên HTTP method | Redirect POST nhưng vẫn giữ POST (không thành GET như 302) |
308 | Permanent Redirect | Như 301 nhưng giữ nguyên HTTP method | API versioning: /v1/users → /v2/users, giữ nguyên POST/PUT |
Cache nuance: Chỉ browser cache 301 và 302 theo mặc định. 307 và 308 không được cache trừ khi có header Cache-Control rõ ràng.
4xx – Lỗi Phía Client (Client Error)
Request có vấn đề – sai cú pháp, thiếu quyền, hoặc resource không tồn tại. Client cần sửa request hoặc xác thực lại.
| Mã | Tên | Nguyên nhân thường gặp | Cách xử lý |
|---|---|---|---|
400 | Bad Request | JSON parse error, thiếu required field, validation fail | Trả về chi tiết lỗi trong body: { "error": "Invalid email format" } |
401 | Unauthorized | Thiếu hoặc token không hợp lệ | Gửi WWW-Authenticate: Bearer header, hướng dẫn client login |
403 | Forbidden | Client xác thực rồi nhưng không có quyền | Không tiết lộ chi tiết (security), chỉ nói “Forbidden” |
404 | Not Found | URL không tồn tại | Trả về page 404 thân thiện, giữ nguyên status 404 (không 200) |
405 | Method Not Allowed | Dùng POST trên endpoint chỉ cho GET | Trả về header Allow: GET, HEAD để client biết method nào hợp lệ |
409 | Conflict | Resource conflict (ví dụ duplicate email) | Dùng cho business logic conflict, khác với 400 validation error |
422 | Unprocessable Entity | Request hợp lệ nhưng semantic error | WebDAV, cũng dùng cho validation business rule |
429 | Too Many Requests | Rate limit vượt ngưỡng | Trả về Retry-After: 60 header, log IP để block nếu cần |
Security tip: Đừng bao giờ trả về stack trace hay chi tiết database error trong 400 hoặc 404. Thông tin đó giúp attacker reconnaissance. Log error internal, trả về generic message cho client.
5xx – Lỗi Phía Server (Server Error)
Máy chủ gặp lỗi khi xử lý request. Đây là lỗi nghiêm trọng – client không thể sửa, phải chờ developer fix.
| Mã | Tên | Nguyên nhân phổ biến | Khi nào dùng |
|---|---|---|---|
500 | Internal Server Error | Exception không được bắt, database down, memory leak | Catch-all khi không biết chính xác lỗi gì. Nên log stack trace. |
502 | Bad Gateway | Proxy (Nginx) không nhận được valid response từ upstream (PHP-FPM, Node) | Upstream crash, timeout, hoặc trả về malformed response |
503 | Service Unavailable | Server quá tải hoặc đang maintenance | Dùng khi có maintenance plan, hoặc auto-scale chưa kịp xử lý traffic |
504 | Gateway Timeout | Upstream không phản hồi trong timeout | Database query chạy quá lâu, external API call timeout |
Production checklist:
- Đừng bao giờ expose stack trace ra client (debug mode off)
- Log đầy đủ context: request ID, user ID, payload
- Trả về
Retry-Afterheader với503nếu biết thời gian maintenance - Đặt monitoring alert cho mọi
5xx> 1% traffic
Code Examples Thực Tế
Dưới đây là cách implement đúng status code trong các framework phổ biến.
Node.js / Express
// POST create user – return 201 with Location header
app.post('/api/users', async (req, res) => {
try {
const user = await User.create(req.body);
res.status(201)
.location(`/api/users/${user.id}`)
.json(user);
} catch (err) {
if (err.code === '23505') { // PostgreSQL duplicate key
return res.status(409).json({ error: 'Email already exists' });
}
res.status(500).json({ error: 'Internal server error' });
}
});
// Rate limiting middleware – return 429
app.use('/api/', rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
handler: (req, res) => {
res.status(429).json({
error: 'Too many requests',
retryAfter: Math.ceil(req.windowMs / 1000)
});
}
}));
PHP / Laravel
// Controller method
public function store(Request $request)
{
$validated = $request->validate([
'email' => 'required|email|unique:users',
'name' => 'required|string|max:255',
]);
$user = User::create($validated);
return response()->json($user, 201)
->header('Location', route('users.show', $user));
}
// Custom exception handler – return 404 for ModelNotFoundException
public function render($request, Throwable $exception)
{
if ($exception instanceof ModelNotFoundException) {
return response()->json(['error' => 'Not found'], 404);
}
return parent::render($request, $exception);
}
Python / FastAPI
from fastapi import FastAPI, HTTPException, Response
from fastapi.responses import JSONResponse
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
try:
db_item = await save_item(item)
return JSONResponse(
status_code=201,
content=db_item.dict(),
headers={"Location": f"/items/{db_item.id}"}
)
except DuplicateError:
raise HTTPException(status_code=409, detail="Item already exists")
@app.get("/items/{item_id}")
async def read_item(item_id: int):
item = await get_item(item_id)
if not item:
raise HTTPException(status_code=404, detail="Item not found")
return item
Nginx Config – Custom Error Pages
# Giữ nguyên status code, serve custom page
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /404.html {
internal; # Chỉ phục vụ nội bộ, không truy cập trực tiếp
add_header Cache-Control "no-cache, must-revalidate";
}
location = /50x.html {
internal;
add_header Cache-Control "no-cache, must-revalidate";
}
Debugging Checklist – Xử Lý Lỗi Hiệu Quả
Khi gặp lỗi, đừng chỉ restart server. Hãy theo checklist này:
- 4xx: Check client request format, headers, auth token, URL typo. Dùng Postman/curl để reproduce.
- 5xx: Check server logs (error_log, application log). Xem stack trace, database connection, memory usage.
- 404 trên API: Kiểm tra route đã register chưa, middleware có block không, CORS preflight có pass không.
- 429: Check rate limiting config, IP reputation (có bị blacklist không), Redis connection nếu dùng distributed rate limit.
- 500: Disable xdebug trong production, enable core dump nếu cần, check PHP error_log hoặc Node console.
Cheatsheet Nhanh – Tra Cứu Trong 5 Giây
| Code | Class | Meaning | Action |
|---|---|---|---|
200 | 2xx | OK | Success, return body |
201 | 2xx | Created | Resource created, send Location header |
204 | 2xx | No Content | Success, no body (DELETE) |
301 | 3xx | Permanent Redirect | SEO-friendly, cacheable |
302 | 3xx | Temporary Redirect | Method may change to GET |
304 | 3xx | Not Modified | Cache hit, no body |
400 | 4xx | Bad Request | Client fix request syntax |
401 | 4xx | Unauthorized | Missing/invalid auth |
403 | 4xx | Forbidden | No permission despite auth |
404 | 4xx | Not Found | URL doesn't exist |
409 | 4xx | Conflict | Business logic conflict |
429 | 4xx | Too Many Requests | Rate limit exceeded |
500 | 5xx | Internal Server Error | Server crash, check logs |
502 | 5xx | Bad Gateway | Proxy upstream invalid response |
503 | 5xx | Service Unavailable | Server overloaded/maintenance |
504 | 5xx | Gateway Timeout | Upstream timeout |
Kết Luận
Mã trạng thái HTTP không chỉ là con số bạn trả về. Chúng là ngôn ngữ chung giữa client và server, là công cụ debug, là yếu tố SEO, và là dấu hiệu của một API well-designed. Khi viết code, hãy luôn hỏi: "Mình đang trả về status code phù hợp chưa?"
Hãy lưu lại cheatsheet này, dùng nó khi code, và bạn sẽ tránh được hàng tá bug cũng như tối ưu được trải nghiệm người dùng. Happy coding! 🐱