---
id: networking-http
title: "HTTP và HTTPS"
shortTitle: "HTTP / HTTPS"
track: networking
trackTitle: "Networking"
order: 4
level: core
status: published
estimatedMinutes: 35
prerequisites:
  - networking-dns
next: networking-tls
reviewedAgainst: 2026-10-12
sources:
  - title: "RFC 9110"
    url: https://www.rfc-editor.org/rfc/rfc9110
  - title: "RFC 9112"
    url: https://www.rfc-editor.org/rfc/rfc9112
---

# HTTP và HTTPS

HTTP là ngôn ngữ client dùng để nói chuyện với app, còn HTTPS là HTTP chạy bên trong TLS. Phần certificate nằm ở bài ngay sau; bài này tập trung vào request, status và header.

## Để làm gì

Sau bài này, bạn đọc được dòng đầu của response, chọn đúng status khi viết health check, và hiểu vì sao thiếu header `Host` thì server có thể trả nhầm site.

## Gồm những gì

- Method `GET`, `POST`, `PUT`, `DELETE`.
- Các nhóm status 2xx, 3xx, 4xx, 5xx.
- Header `Host`, và HTTP với HTTPS khác nhau ở tầng nào.

Bài này không nói về HTTP/2 frame hay HTTP/3. Bạn chỉ cần biết chúng giữ nguyên semantics của RFC 9110, chỉ đổi cách đóng gói dữ liệu trên mạng.

## Dùng khi nào

Bạn cần đọc HTTP khi viết probe, cấu hình reverse proxy và xem access log. Nginx và Apache ở các bài sau đều chuyển tiếp request HTTP, nên bạn phải đọc được dòng đầu của response trước khi sửa `proxy_pass`.

Đừng để health check trả `200` khi dependency đã chết, vì load balancer sẽ tiếp tục đẩy user vào máy đó.

## Kiến thức cốt lõi

Dòng đầu của request gồm method, path và version. `GET` lấy representation của tài nguyên và không yêu cầu server đổi dữ liệu. `POST` gửi nội dung để server xử lý. `PUT` thay thế tài nguyên tại URL đó. `DELETE` yêu cầu xóa tài nguyên.

Status là một số có ba chữ số. `200` nghĩa là thành công và có representation trả về. `201` là vừa tạo mới. `301` và `302` bảo client đi URL khác. `400` là request sai. `401` là chưa xác thực. `403` là server đã biết bạn là ai nhưng bạn không có quyền. `404` là không có tài nguyên đó. `500` là server lỗi. `502` và `503` thường gặp khi proxy không nói chuyện được với upstream. `504` là upstream không trả lời kịp trong thời hạn.

Header `Host` là bắt buộc với HTTP/1.1. Một IP có thể phục vụ nhiều site, nên thiếu `Host` thì server không biết chọn virtual host nào.

HTTPS không đổi method hay status. Handshake TLS phải xong trước, rồi bên trong kênh đó mới là HTTP. Vì thế khi certificate sai, connection TCP có thể đã thành công, nhưng curl báo lỗi trước khi có status nào.

## Lệnh và code mẫu

```bash
curl.exe -sS -D - -o NUL https://example.com/
curl.exe -sS -o NUL -w "%{http_code}\n" https://example.com/does-not-exist
```

Lệnh trên viết cho PowerShell trên Windows. Trên Linux, macOS hoặc WSL, bỏ `.exe` và đổi `NUL` thành `/dev/null`.

## Thực hành

1. Gọi trang chủ `example.com` và đọc status ở dòng đầu.
2. Gọi một path không tồn tại và đọc status.
3. Đừng gửi request tới API nội bộ của công ty trong lab này.

## Kết quả mong đợi

`https://example.com/` trả `200`, còn path không tồn tại trả `404`.

## Checklist

- [ ] Health check trả lỗi khi một dependency bắt buộc đã chết, không cố giữ `200`.
- [ ] Không dùng lẫn `401` với `403` trong log.
- [ ] Proxy log cả status của upstream, không chỉ status nó trả cho client.

## Thẻ ôn

### networking-http-auth
kind: distinguish
cue: `401` là chưa xác thực, server chưa biết bạn là ai. `403` là đã biết bạn là ai nhưng bạn không có quyền. Đừng ghi lẫn hai số trong log.

Q: Một API trả `401` cho request thiếu token và `403` cho user đã đăng nhập nhưng không được xóa tài nguyên. Hai mã khác nhau ở bước nào, và ghi lẫn trong log thì người trực sẽ chẩn đoán sai thế nào?

A: `401` nghĩa là chưa xác thực. Server không chấp nhận danh tính đi kèm request, hoặc request không có danh tính. Việc cần làm là đăng nhập hoặc gửi credential đúng. `403` nghĩa là xác thực đã xong, server biết bạn là ai, và từ chối vì bạn không có quyền với việc đó. Ghi lẫn hai số thì một lỗi hết hạn token sẽ bị đọc thành lỗi phân quyền, hoặc ngược lại, và người trực đi sửa nhầm chỗ. Giữ đúng mã trong log và trong tài liệu API.

### networking-http-health
kind: trap
cue: Health check vẫn trả `200` khi dependency bắt buộc đã chết thì load balancer tiếp tục đổ user vào máy đó.

Q: Database mà app bắt buộc phải có đã chết. Đường `/health` vẫn trả `200` vì process còn sống. Load balancer sẽ làm gì với máy này?

A: Nó coi máy còn khỏe và tiếp tục gửi user vào. User tới app, app không nói chuyện được với database, và lỗi hiện ra với người dùng thay vì bị đưa sang máy khác. Health check không phải "process còn listen". Nó phải trả lỗi khi một dependency bắt buộc đã chết, để balancer rút máy đó khỏi vòng. Check luôn xanh là check không bảo vệ được ai.

### networking-http-host
kind: default
cue: HTTP/1.1 không có header `Host` thì server một IP nhiều site không biết chọn virtual host nào, và có thể trả nhầm site.

Q: Một server dùng chung một IP cho nhiều site, phân biệt bằng virtual host. Client HTTP/1.1 gửi request không có header `Host`. Server chọn site nào?

A: Nó không có tên để phân biệt các site đang dùng chung IP đó. Virtual host dựa trên `Host`. Thiếu header, server rơi vào lựa chọn mặc định và có thể trả nhầm site, hoặc từ chối. `Host` là bắt buộc với HTTP/1.1, không phải header trang trí. Khi gọi bằng IP hoặc bằng một công cụ tự nối, hãy gửi đúng `Host` của site định vào.

### networking-http-redirect
kind: trap
cue: API `POST` đừng dựa vào redirect `301` để sửa URL. Client đi theo redirect có thể đổi method. Trả lỗi rõ thì hơn.

Q: Một API nhận `POST` và trả `301` để bảo client gọi URL mới. Có dựa vào redirect đó cho an toàn không? TCP đã nối thì HTTPS đã xong chưa?

A: Đừng dựa vào redirect đó để giữ nguyên thao tác. Khi đi theo `301`, client được phép đổi method, và một `POST` có thể thành `GET` ở URL mới, mất thân request. Với API nội bộ, trả một lỗi nói rõ URL đúng, để người gọi sửa. Một việc khác hay bị gộp nhầm: TCP đã nối chưa phải HTTPS xong. TLS còn đứng giữa TCP và HTTP. Nối được cổng chưa có nghĩa là chứng chỉ và request đã đi.

## Lỗi thường gặp

**Dùng redirect `301` cho một API `POST`.** Khi đi theo redirect, client có thể đổi method. Với API nội bộ, hãy trả lỗi rõ ràng, hoặc dùng mã mà client của bạn đã được viết để xử lý. Đừng dựa vào redirect để sửa URL của `POST`.

**Thấy TCP kết nối được rồi kết luận HTTPS ổn.** TCP xong mới tới TLS, rồi mới tới HTTP. `curl -v` cho bạn thấy request dừng ở bước nào.

## Nguồn

1. [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110) — method, status, header `Host`.
2. [RFC 9112](https://www.rfc-editor.org/rfc/rfc9112) — HTTP/1.1 trên kết nối.

## Tiếp theo

[TLS certificate](./06-tls.md)
