Khi thiết kế RESTful API, một thói quen rất phổ biến của nhiều backend developer là luôn trả về HTTP 200 OK cho mọi request thành công, kèm theo một JSON rỗng kiểu {} hoặc { "success": true }.
Cách làm này không gây lỗi logic ứng dụng, nhưng lại vi phạm chuẩn thiết kế HTTP và bỏ lỡ cơ hội tối ưu tài nguyên mạng ở quy mô lớn. Chuẩn RFC 9110 định nghĩa sẵn một mã trạng thái riêng biệt cho trường hợp này: HTTP 204 No Content.
1. HTTP 204 No Content là gì?
HTTP 204 là mã trạng thái thành công (2xx), xác nhận server đã xử lý request hoàn tất nhưng cố tình không trả về bất kỳ dữ liệu nào trong response body.
Điểm đặc biệt của status 204:
- Response hoàn toàn không có body (kể cả 0 bytes body, không có
Content-Type, không cóContent-Length). - Trình duyệt hoặc HTTP Client khi nhận mã 204 sẽ giữ nguyên góc nhìn hiện tại (không refresh hay điều hướng trang).
- Tiết kiệm băng thông tối đa vì gói tin phản hồi chỉ chứa mỗi phần header siêu nhẹ.
2. Khi nào nên dùng 204 và khi nào nên dùng 200?
- Xóa tài nguyên (
DELETE /api/users/123): Người dùng xóa một item thành công, giao diện phía client thường chỉ cần cập nhật danh sách local hoặc ẩn item đó đi. Việc backend trả về{ message: "Deleted successfully" }cùng mã 200 là thao tác thừa thãi. Hãy dùng 204. - Cập nhật trạng thái không cần dữ liệu mới (
PUT/PATCH): Ví dụ đánh dấu đã đọc thông báo (PUT /api/notifications/read-all). Nếu client không cần lấy lại toàn bộ danh sách vừa sửa, trả về 204 là lựa chọn sạch nhất. - Thực thi tác vụ nền hoặc gửi log (
POST /api/analytics): Client gửi dữ liệu tracking hành vi hoặc heartbeat ping lên server, server chỉ cần xác nhận “đã nhận” mà không cần gửi phản hồi gì thêm. - Đối chiếu nhanh:
- Trả về dữ liệu vừa tạo/sửa? ➔ Dùng
200 OKhoặc201 Created(kèm body). - Thao tác thành công nhưng client không cần body? ➔ Dùng
204 No Content.
- Trả về dữ liệu vừa tạo/sửa? ➔ Dùng
3. Cách triển khai thực tế trên Node.js (Express & Fastify)
- Trên Express.js:
JavaScript
// Thay vì cách cũ:
app.delete('/api/orders/:id', async (req, res) => {
await orderService.deleteOrder(req.params.id);
// Cũ: res.status(200).json({ success: true });
// Chuẩn REST:
res.status(204).end(); // Không truyền tham số vào end()
});
- Trên Fastify:
JavaScript
fastify.delete('/api/orders/:id', async (request, reply) => {
await orderService.deleteOrder(request.params.id);
return reply.code(204).send();
});
4. Bẫy cần tránh khi dùng HTTP 204
- Vẫn cố nhét body: Nhiều dev viết
res.status(204).json({ data: null }). Một số web server hoặc proxy (như Nginx, Cloudflare) sẽ tự động cắt bỏ phần body này hoặc ném cảnh báo vì response 204 theo chuẩn không được phép chứa content. - Frontend bắt lỗi parse JSON: Nếu code frontend dùng fetch API và mặc định gọi
.json()sau mỗi request:
JavaScript
const res = await fetch('/api/orders/123', { method: 'DELETE' });
const data = await res.json(); // ❌ Ném lỗi 'Unexpected end of JSON input' vì status 204 không có body!
Cách xử lý: Luôn kiểm tra status trước khi parse:
JavaScript
if (res.status === 204) return true;
return await res.json();
Chuẩn hóa từng status code nhỏ không chỉ giúp API của bạn đồng bộ với các chuẩn quốc tế, mà còn giúp team Frontend dễ dàng nắm bắt ngữ nghĩa mà không cần đọc từng dòng payload.


