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, 404 khiế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 Forbidden khác với 404 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 Created thay 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.

TênÝ nghĩaUse case
100ContinueMáy chủ đồng ý nhận request bodyUpload file lớn, client gửi Expect: 100-continue header
101Switching ProtocolsChuyển sang protocol khác (WebSocket upgrade)WebSocket handshake: Upgrade: websocket
102ProcessingServer đ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.

TênÝ nghĩaVí dụ thực tế
200OKStandard success responseGET /api/users trả về JSON array
201CreatedResource được tạo mớiPOST /api/users tạo user mới, trả về user object với ID
202AcceptedRequest đã chấp nhận nhưng chưa xử lý xongPOST /jobs để tạo background job, trả về job_id ngay
204No ContentThành công nhưng không có bodyDELETE /api/users/123 – xóa xong, không cần trả về gì
206Partial ContentChỉ 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.

TênÝ nghĩaKhi nào dùng
301Moved PermanentlyURL đã di chuyển vĩnh viễnThay đổi URL slug, chuyển HTTP → HTTPS, canonical redirect
302FoundTemporary redirect (method có thể đổi)A/B testing, maintenance page, login flow
303See OtherRedirect bằng GET, dù request gốc là POSTSau khi submit form, redirect về trang kết quả với GET
304Not ModifiedKhi gửi If-Modified-Since hoặc If-None-Match và resource chưa đổi
307Temporary RedirectNhư 302 nhưng giữ nguyên HTTP methodRedirect POST nhưng vẫn giữ POST (không thành GET như 302)
308Permanent RedirectNhư 301 nhưng giữ nguyên HTTP methodAPI versioning: /v1/users → /v2/users, giữ nguyên POST/PUT

Cache nuance: Chỉ browser cache 301302 theo mặc định. 307308 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.

TênNguyên nhân thường gặpCách xử lý
400Bad RequestJSON parse error, thiếu required field, validation failTrả về chi tiết lỗi trong body: { "error": "Invalid email format" }
401UnauthorizedThiếu hoặc token không hợp lệGửi WWW-Authenticate: Bearer header, hướng dẫn client login
403ForbiddenClient xác thực rồi nhưng không có quyềnKhông tiết lộ chi tiết (security), chỉ nói “Forbidden”
404Not FoundURL không tồn tạiTrả về page 404 thân thiện, giữ nguyên status 404 (không 200)
405Method Not AllowedDùng POST trên endpoint chỉ cho GETTrả về header Allow: GET, HEAD để client biết method nào hợp lệ
409ConflictResource conflict (ví dụ duplicate email)Dùng cho business logic conflict, khác với 400 validation error
422Unprocessable EntityRequest hợp lệ nhưng semantic errorWebDAV, cũng dùng cho validation business rule
429Too Many RequestsRate limit vượt ngưỡngTrả 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.

TênNguyên nhân phổ biếnKhi nào dùng
500Internal Server ErrorException không được bắt, database down, memory leakCatch-all khi không biết chính xác lỗi gì. Nên log stack trace.
502Bad GatewayProxy (Nginx) không nhận được valid response từ upstream (PHP-FPM, Node)Upstream crash, timeout, hoặc trả về malformed response
503Service UnavailableServer quá tải hoặc đang maintenanceDùng khi có maintenance plan, hoặc auto-scale chưa kịp xử lý traffic
504Gateway TimeoutUpstream không phản hồi trong timeoutDatabase 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-After header với 503 nế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

CodeClassMeaningAction
2002xxOKSuccess, return body
2012xxCreatedResource created, send Location header
2042xxNo ContentSuccess, no body (DELETE)
3013xxPermanent RedirectSEO-friendly, cacheable
3023xxTemporary RedirectMethod may change to GET
3043xxNot ModifiedCache hit, no body
4004xxBad RequestClient fix request syntax
4014xxUnauthorizedMissing/invalid auth
4034xxForbiddenNo permission despite auth
4044xxNot FoundURL doesn't exist
4094xxConflictBusiness logic conflict
4294xxToo Many RequestsRate limit exceeded
5005xxInternal Server ErrorServer crash, check logs
5025xxBad GatewayProxy upstream invalid response
5035xxService UnavailableServer overloaded/maintenance
5045xxGateway TimeoutUpstream 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! 🐱

Chào các bạn mình là Quốc Hùng , mình sinh ra thuộc cung song tử ,song tử luôn khẳng định chính mình ,luôn luôn phấn đấu vượt lên phía trước ,mình sinh ra và lớn lên tại vùng đất võ cổ truyền ,đam mê của mình là coder ,ngày đi học tối về viết blog ...