5.1 HTTP Request/Response Anatomy
Request
GET /api/users/42 HTTP/1.1 ← Request line: method path version
Host: api.example.com ← Headers
Accept: application/json
Authorization: Bearer eyJhbGc...
User-Agent: curl/7.64.1
Cookie: session=abc123
(empty line)
← (Body — GET thường không có)
Response
HTTP/1.1 200 OK ← Status line: version code reason
Content-Type: application/json ← Headers
Content-Length: 87
Cache-Control: max-age=3600
Server: nginx/1.21
Date: Sat, 09 May 2026 10:00:00 GMT
{"id": 42, "name": "Alice", "email": "..."} ← Body
3 phần
- Start line: request line / status line
- Headers: key-value pairs, ngăn cách bởi
:, kết thúc bằng dòng trống - Body: data (tuỳ chọn)
Toàn bộ là plain text ASCII trong HTTP/1.1 — bạn có thể telnet đến port 80 và gõ tay request.
5.2 HTTP Methods
| Method | Mục đích | Body | Safe | Idempotent | Cacheable |
|---|---|---|---|---|---|
| GET | Đọc resource | Không | ✓ | ✓ | ✓ |
| HEAD | Như GET nhưng chỉ headers | Không | ✓ | ✓ | ✓ |
| POST | Tạo mới / submit | Có | ✗ | ✗ | Hiếm |
| PUT | Replace toàn bộ resource | Có | ✗ | ✓ | ✗ |
| PATCH | Update một phần | Có | ✗ | ~ | ✗ |
| DELETE | Xoá resource | Tuỳ | ✗ | ✓ | ✗ |
| OPTIONS | Hỏi server method nào support (CORS preflight) | Không | ✓ | ✓ | ✗ |
| CONNECT | Tunnel (HTTPS proxy) | — | ✗ | ✗ | ✗ |
| TRACE | Echo request (debug) | Không | ✓ | ✓ | ✗ |
Phân biệt POST vs PUT vs PATCH
- POST /users: tạo user mới — server tự sinh ID. Không idempotent (gọi 2 lần → 2 user).
- PUT /users/42: replace toàn bộ user 42 với data trong body. Idempotent (gọi nhiều lần kết quả giống nhau).
- PATCH /users/42: update 1 vài field của user 42. Theo lý thuyết không idempotent, nhưng nếu client gửi field cụ thể (vd
email=x) thì tương đương idempotent.
5.3 HTTP Status Codes — phải thuộc
1xx — Informational
100 Continue— server bảo client gửi tiếp body101 Switching Protocols— vd upgrade lên WebSocket
2xx — Success
200 OK— thành công, có body201 Created— POST thành công, resource được tạo202 Accepted— đã nhận request, xử lý async204 No Content— thành công, không có body (vd DELETE)206 Partial Content— response của Range request
3xx — Redirection
301 Moved Permanently— vĩnh viễn, browser cache302 Found— tạm thời (đôi khi gọi302 Moved Temporarily)304 Not Modified— client có cache hợp lệ, không cần download lại307 Temporary Redirect— như 302 nhưng giữ method (POST vẫn là POST)308 Permanent Redirect— như 301 nhưng giữ method
4xx — Client Error
400 Bad Request— request sai cú pháp / data không hợp lệ401 Unauthorized— chưa auth (cần login)403 Forbidden— đã auth nhưng không có quyền404 Not Found— resource không tồn tại405 Method Not Allowed— method không support cho resource này409 Conflict— vd update với version cũ (optimistic lock)410 Gone— như 404 nhưng "đã từng tồn tại, đã xoá"413 Payload Too Large— body quá lớn422 Unprocessable Entity— syntax OK nhưng semantic sai (validation)429 Too Many Requests— rate limit
5xx — Server Error
500 Internal Server Error— unhandled exception phía server501 Not Implemented— server không support method/feature502 Bad Gateway— proxy nhận response invalid từ upstream503 Service Unavailable— server quá tải / maintenance504 Gateway Timeout— proxy timeout chờ upstream
- 401 Unauthorized: thực ra nghĩa là "Unauthenticated" — chưa login
- 403 Forbidden: đã login nhưng không có permission
- 301 Permanent: search engine cập nhật URL mới, page rank chuyển sang URL mới
- 302 Temporary: search engine không cập nhật, vẫn giữ URL cũ
5.4 Headers Quan Trọng
Request Headers
Host: tên domain (bắt buộc HTTP/1.1) — cho phép virtual hosting (nhiều site trên 1 IP)User-Agent: định danh client (browser, curl, app...)Accept: client chấp nhận content type nào (vdapplication/json)Accept-Encoding: client decode được gì (gzip, br)Accept-Language: ngôn ngữ ưu tiênAuthorization: credentials (Bearer token, Basic auth)Cookie: gửi cookie client đang lưuContent-Type: type của body (POST/PUT)Content-Length: kích thước bodyReferer(sic): URL trang trướcOrigin: origin của request (CORS)If-None-Match: ETag client đang có (cho 304)
Response Headers
Content-Type: type body trả vềContent-Length: kích thước bodyContent-Encoding: nén gì (gzip, br)Cache-Control: cache strategy (max-age, no-cache, no-store, public, private)ETag: identifier version của resourceLast-Modified: thời gian update cuốiSet-Cookie: bảo browser lưu cookieLocation: URL redirect (3xx)Access-Control-Allow-Origin: CORSStrict-Transport-Security(HSTS): bắt browser luôn HTTPSContent-Security-Policy(CSP): chống XSS
5.5 HTTP/1.1 (1997) — text-based, vẫn phổ biến
Đặc điểm
- Plain text — đọc được bằng telnet
- Mỗi request/response 1 cặp — synchronous trên 1 connection
- Persistent connection (keep-alive default) — tái sử dụng TCP cho nhiều request
- Pipelining (lý thuyết): gửi nhiều request không đợi response — nhưng response vẫn phải theo thứ tự (HOL block) → ít browser dùng
Vấn đề HTTP/1.1
- Head-of-line blocking ở app layer: 1 connection chỉ xử lý 1 request tại 1 thời điểm
- Browser mở 6 TCP connection song song để workaround → tốn server resource
- Header lặp lại mỗi request → overhead
- Text format → parse chậm
Demo telnet
telnet example.com 80
Trying 93.184.216.34...
Connected to example.com.
GET / HTTP/1.1
Host: example.com
[gõ Enter 2 lần]
HTTP/1.1 200 OK
Content-Type: text/html
...
5.6 HTTP/2 (2015) — binary, multiplexed
Kế thừa Google SPDY. Giải quyết các vấn đề HTTP/1.1.
4 đổi mới chính
1. Binary framing thay text
Thay vì plaintext, HTTP/2 dùng binary frame — parse nhanh hơn, ít error hơn.
2. Multiplexing — nhiều stream trên 1 TCP connection
Cùng 1 TCP, nhiều request "đan xen" qua các stream. Không cần mở nhiều TCP. Không có HOL ở app layer (nhưng vẫn có HOL ở TCP — sẽ giải quyết ở HTTP/3).
3. Header compression — HPACK
Nhiều request có headers giống nhau (cookie, user-agent...). HPACK dùng dictionary + Huffman coding nén headers, giảm rất nhiều bandwidth.
4. Server Push (ít dùng, đã deprecate ở 1 số browser)
Server có thể "đẩy" resource liên quan trước khi client request. Vd: gửi style.css luôn khi client request index.html.
Thực tế ít hiệu quả → Chrome đã bỏ support 2022. Thay bằng 103 Early Hints.
Vẫn còn vấn đề: TCP HOL blocking
Nhiều stream trên 1 TCP — nếu 1 packet TCP mất, tất cả stream đều bị block đợi retransmit (đã giải thích ở Chương 4).
5.7 HTTP/3 (2022) — chạy trên QUIC
Giải quyết TCP HOL blocking bằng cách... bỏ TCP! Dùng QUIC trên UDP.
Lợi ích so với HTTP/2
- Per-stream HOL: 1 stream mất packet không block stream khác
- 0-RTT connection với resumption (HTTP/2 + TLS = 3 RTT setup)
- Connection migration: đổi mạng (Wi-Fi → 4G) không phải reconnect
- Built-in TLS 1.3: HTTPS bắt buộc
Adoption
Cloudflare, Google, Facebook đã enable HTTP/3 từ 2020+. Chrome, Firefox, Safari, curl 7.66+ đều support.
curl --http3 https://www.cloudflare.com -I
# HTTP/3 200
# server: cloudflare
# ...
So sánh 3 phiên bản
| Tính chất | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| Năm | 1997 | 2015 | 2022 |
| Format | Text | Binary | Binary |
| Transport | TCP | TCP | QUIC (UDP) |
| Multiplex | Không (cần nhiều TCP) | Có | Có |
| HOL blocking | App level | TCP level | Không |
| Header compression | Không | HPACK | QPACK |
| Setup latency | 1 RTT TCP + 2 RTT TLS = 3 RTT | 1 + 2 = 3 RTT | 0-1 RTT |
5.8 Idempotency & Safety — phải hiểu đúng
Định nghĩa
- Safe = không thay đổi state server. GET, HEAD, OPTIONS.
- Idempotent = gọi nhiều lần kết quả giống gọi 1 lần. GET, PUT, DELETE, HEAD.
- POST = không idempotent (mỗi lần POST tạo 1 row mới)
- PATCH = thường idempotent nếu là "set value" thay vì "increment"
Tại sao quan trọng?
Network không tin cậy. Request có thể fail giữa chừng — client không biết server đã xử lý chưa. Retry là pattern phổ biến.
Retry idempotent operation an toàn (gọi 2 lần = 1 lần). Retry non-idempotent (POST) có thể tạo duplicate.
Solution: Idempotency Key
POST /api/payment HTTP/1.1
Idempotency-Key: 8e7c4f2a-a8c1-4e58-9b7c-...
Content-Type: application/json
{"amount": 100, "currency": "USD"}
Server lưu key + result. Nếu request lại với cùng key, trả result cũ → không tạo payment thứ 2. Stripe, PayPal đều dùng pattern này.
5.9 HTTP Caching — giảm bandwidth, tăng tốc
Cache-Control header
Cache-Control: public, max-age=3600, immutable
Cache-Control: no-cache # phải revalidate mỗi lần
Cache-Control: no-store # không được cache (sensitive data)
Cache-Control: private # chỉ cache trên browser, không proxy
ETag — version identifier
Server gửi ETag cùng response. Lần sau client gửi If-None-Match: <etag>. Nếu resource không đổi → server trả 304 Not Modified không có body → tiết kiệm bandwidth.
# Lần đầu
GET /api/users/42
→ 200 OK
ETag: "abc123"
{"id": 42, "name": "Alice"}
# Lần sau
GET /api/users/42
If-None-Match: "abc123"
→ 304 Not Modified
(không có body)
Last-Modified — cũ hơn ETag
GET /api/users/42
If-Modified-Since: Sat, 09 May 2026 10:00:00 GMT
→ 304 Not Modified hoặc 200 OK
Cache hierarchy
- Browser memory cache (mới nhất, nhanh nhất)
- Browser disk cache
- Service worker cache
- CDN edge cache
- Reverse proxy cache (nginx, Varnish)
- Origin server
5.10 Compression — giảm kích thước
HTTP body có thể nén để giảm bandwidth.
Request:
Accept-Encoding: gzip, deflate, br
Response:
Content-Encoding: gzip
(body đã nén)
Algorithms
- gzip: phổ biến nhất, ~50-70% giảm cho text
- deflate: ít dùng (lý do lịch sử)
- brotli (br): hơn gzip 15-20% cho text, do Google. Hỗ trợ tốt trên modern browser
- zstd: mới hơn, đang được adopt
Khi nào KHÔNG nén?
- Image, video — đã nén sẵn
- Sensitive data (vd JWT trong body) — có thể leak qua side channel (BREACH attack)
- Body nhỏ < ~1KB — overhead lớn hơn lợi ích
Bài tập
Mở terminal, gõ telnet example.com 80. Gõ GET / HTTP/1.1, Enter, Host: example.com, Enter 2 lần. Quan sát response.
Mỗi case sau, server nên trả status nào? (a) Login sai password; (b) Submit form với email bị trùng; (c) Endpoint POST đến URL chỉ chấp nhận GET; (d) DB bị down; (e) Image quá lớn.
Cho REST API quản lý sản phẩm: viết URL + method cho từng case: (a) Tạo sản phẩm mới; (b) Update toàn bộ thông tin sản phẩm 5; (c) Đổi giá sản phẩm 5; (d) Xoá sản phẩm 5.
Mở Chrome DevTools → Network → enable cột "Protocol". Browse google.com vs 1 trang nhỏ tự host. So sánh: protocol nào, số connection.
Viết Express endpoint trả JSON với header ETag. Test: lần 1 nhận 200; lần 2 với If-None-Match nhận 304.
Thiết kế API POST /payments support Idempotency-Key. Pseudocode lưu key trong Redis với TTL 24h.
Chạy curl --http3 https://cloudflare.com -I. Có ra HTTP/3 không? Nếu chưa, chrome://net-export hoặc Firefox protocol indicator.
🧪 Quiz cuối chương
Câu 1. Phân biệt 401 và 403?
Đáp án: chưa auth vs không có quyền. Tên 401 "Unauthorized" gây hiểu nhầm — thực ra là "Unauthenticated".
Câu 2. 301 vs 302 khác nhau ở đâu?
Đáp án: vĩnh viễn vs tạm thời. Quan trọng cho SEO. Sai 302 thay 301 → mất page rank.
Câu 3. Method nào idempotent?
Đáp án: GET, PUT, DELETE, HEAD. POST tạo mới mỗi lần → không idempotent. PATCH thường nhưng không bắt buộc.
Câu 4. HTTP/2 nhanh hơn HTTP/1.1 ở đâu?
Đáp án: multiplexing + binary + HPACK. HTTP/1.1 1 connection 1 request → cần mở 6 TCP. HTTP/2 1 TCP nhiều stream.
Câu 5. HTTP/3 chạy trên?
Đáp án: QUIC. Tránh TCP head-of-line blocking. 0-RTT, connection migration.
Câu 6. ETag để làm gì?
Đáp án: cache validation. Client gửi If-None-Match: <etag>; server trả 304 nếu khớp.
Câu 7. Tại sao GET nên idempotent & safe?
Đáp án: cache + retry + crawl an toàn. Nếu GET tạo state → search engine crawl link sẽ gây vô số side effect.
Câu 8. Khi nào dùng PUT vs POST?
Đáp án: POST tạo mới, PUT replace. POST /users server cấp ID; PUT /users/42 client biết ID, replace toàn bộ.
Tổng kết chương 5
- ✅ HTTP request/response: start line + headers + body
- ✅ Methods: GET (read), POST (create), PUT (replace), PATCH (update partial), DELETE
- ✅ Status codes: 2xx success, 3xx redirect, 4xx client error, 5xx server error
- ✅ Bẫy 401 (chưa auth) vs 403 (không quyền); 301 (perm) vs 302 (temp)
- ✅ HTTP/1.1: text, persistent connection, head-of-line ở app
- ✅ HTTP/2: binary, multiplexing, HPACK — vẫn TCP HOL
- ✅ HTTP/3: trên QUIC/UDP, per-stream HOL, 0-RTT, connection migration
- ✅ Idempotency Key: pattern an toàn cho retry POST
- ✅ Cache:
Cache-Control,ETag+If-None-Match→ 304 - ✅ Compression: gzip, brotli — không nén media/sensitive data