Skip to content

HTTP Status Code ​

Status code là ngôn ngữ hợp đồng quan trọng nhất giữa API và mọi bên tiêu thụ nó (client, proxy, cache, monitoring tool) — chúng đọc status code để biết cách xử lý mà không cần đọc response body. Dùng sai/không nhất quán khiến việc debug và tích hợp tốn kém hơn nhiều so với vẻ ngoài "chỉ là 1 con số".

5 nhóm status code ​

NhómÝ nghĩaGhi chú
1xxThông tin tạm thờiHiếm khi cần quan tâm ở tầng ứng dụng
2xxThành công200 OK, 201 Created, 204 No Content
3xxChuyển hướng301/302 redirect, 304 Not Modified (cache)
4xxLỗi phía clientRequest sai/thiếu quyền/không tồn tại — client cần sửa request, không nên retry y nguyên
5xxLỗi phía serverServer gặp sự cố khi xử lý — client có thể retry sau (nhất là với idempotency key)

Một API CRUD thiết kế tốt thường chỉ cần khoảng 15 status code trong toàn bộ hệ thống — không cần cố dùng hết bảng mã HTTP.

Bảng dùng nhanh cho CRUD ​

Tình huốngStatus code
Đọc thành công200 OK
Tạo mới thành công201 Created
Thao tác thành công, không có nội dung trả về (ví dụ xoá)204 No Content
Request sai định dạng/thiếu trường bắt buộc400 Bad Request
Chưa xác thực (không có/token sai)401 Unauthorized
Đã xác thực nhưng không đủ quyền403 Forbidden
Resource không tồn tại404 Not Found
Xung đột dữ liệu (ví dụ trùng email khi đăng ký)409 Conflict
Dữ liệu đúng định dạng nhưng vi phạm business rule422 Unprocessable Entity
Gọi quá số lần cho phép trong khoảng thời gian (rate limit)429 Too Many Requests
Lỗi không lường trước ở server500 Internal Server Error

Rate limiting — vì sao cần và cách báo cho client biết ​

Rate limiting là giới hạn số request 1 client (theo user, theo API key, hoặc theo IP) được gọi trong 1 khoảng thời gian — bảo vệ server khỏi bị quá tải (vô tình do bug client gọi lặp, hoặc cố ý do tấn công/brute-force). Khi vượt giới hạn, server trả 429 Too Many Requests, thường kèm header Retry-After (số giây client nên đợi trước khi gọi lại). Nên áp dụng rate limiting ít nhất cho các endpoint nhạy cảm (đăng nhập, gửi OTP...) ngay từ đầu, không đợi tới khi bị lạm dụng mới thêm.

Ví dụ minh hoạ — 1 luồng tạo resource hoàn chỉnh ​

Request:

POST /users
Content-Type: application/json

{
  "email": "user@example.com",
  "name": "Nguyen Van A"
}

Response thành công:

HTTP/1.1 201 Created
Content-Type: application/json
Location: /users/123

{
  "id": "123",
  "email": "user@example.com",
  "name": "Nguyen Van A",
  "createdAt": "2026-08-09T10:00:00Z"
}

Response khi validate lỗi:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Du lieu khong hop le",
    "details": [
      { "field": "email", "message": "Email khong dung dinh dang" }
    ]
  }
}

Chú ý: status code (201, 400) đi kèm 1 body JSON mô tả chi tiết — client dựa vào status code để biết loại kết quả (thành công/lỗi), rồi đọc body để biết chi tiết (dữ liệu tạo ra, hoặc lý do lỗi cụ thể).

Sai lầm thường gặp ​

  • Lỗi validate dữ liệu trả về 5xx thay vì 4xx — validate sai là lỗi của request (client), không phải lỗi server. Trả 5xx cho lỗi validate khiến client hiểu nhầm là "cứ retry sẽ hết lỗi", trong khi thực tế phải sửa dữ liệu gửi lên.
  • Nhầm 401 và 403: 401 = chưa biết bạn là ai (thiếu/sai thông tin xác thực); 403 = đã biết bạn là ai nhưng không đủ quyền làm việc này. Dùng sai khiến client không biết nên "đăng nhập lại" hay "xin quyền".
  • Lộ thông tin nhạy cảm trong response lỗi — không nên trả nguyên stack trace, câu lệnh SQL, hay chi tiết implementation trong message lỗi trả về client; chỉ nên trả thông điệp chung chung, log chi tiết ở phía server.
  • Format lỗi không nhất quán giữa các endpoint — mỗi endpoint tự bịa 1 kiểu JSON lỗi khác nhau khiến client phải xử lý riêng từng trường hợp. Chuẩn khuyến nghị hiện nay là RFC 9457 Problem Details — một format JSON thống nhất cho lỗi API (nội dung chi tiết về chuẩn hoá lỗi và Exception Filter sẽ nằm ở note "Exception Handling" riêng, vì đây là chủ đề tuần kế tiếp trong plan).