# Vòng đời Request HTTP: Từ URL đến phản hồi (/vi/docs/web-platform/http-request-lifecycle)



# Vòng đời Request HTTP: Từ URL đến phản hồi [#vòng-đời-request-http-từ-url-đến-phản-hồi]

## Tóm tắt nhanh (TL;DR) [#tóm-tắt-nhanh-tldr]

Vào ngày cao điểm mua sắm, trang thanh toán ghi nhận độ trễ tăng vọt lên tới 1,2 giây đối với người dùng mới truy cập lần đầu, trong khi khách hàng thân thiết thao tác mượt mà chỉ mất 80ms. Đội ngũ kỹ thuật đổ lỗi cho cơ sở dữ liệu chậm, nhưng dữ liệu APM cho thấy câu lệnh database chỉ tốn chưa đầy 15ms. Thủ phạm thực sự chính là cơn bão cold request: phân giải DNS đệ quy, bắt tay 3 bước TCP, thương lượng TLS 1.3, trượt cache CDN và khởi tạo kết nối mới trên từng lời gọi fetch do thiếu cơ chế connection pooling.

> 💡 &#x2A;*Quy tắc bỏ túi:** &#x2A;*Một HTTP request là một phiên hội thoại ngữ nghĩa, không đồng nghĩa với một kết nối mạng.** Một request có thể hoàn thành trọn vẹn ngay trong bộ nhớ client (cache hit), đi qua socket mở sẵn chỉ trong 1-RTT, hoặc phải trả giá bằng hàng loạt round-trip đắt đỏ qua DNS, TCP và TLS nếu kết nối không được tái sử dụng.

* **Hợp đồng ngữ nghĩa đối chiếu với đường ống truyền tải:** HTTP chuẩn hóa ngữ nghĩa trao đổi request/response (RFC 9110); các tầng transport (TCP, TLS, QUIC/UDP) cung cấp luồng byte vật lý bên dưới.
* **Bốn hành trình phân nhánh sớm:** Khởi tạo request rẽ nhánh rất sớm: trúng cache client (không tốn mạng), tái sử dụng kết nối ấm (không tốn bắt tay), trúng CDN trung gian (không tốn tài nguyên máy chủ gốc), hoặc thiết lập lạnh toàn phần.
* **Ghép kênh qua các thế hệ giao thức:** HTTP/1.1 giới thiệu tái sử dụng kết nối keep-alive; HTTP/2 bổ sung ghép kênh nhị phân trên một luồng TCP duy nhất; HTTP/3 xóa bỏ tắc nghẽn đầu hàng (head-of-line blocking) bằng QUIC trên nền UDP.
* **Tái xác thực bộ nhớ đệm:** Proxy và CDN trung gian kiểm tra các tiêu đề `Cache-Control`, `ETag` và `304 Not Modified` trước khi chuyển tiếp dữ liệu đến mã nguồn backend.
* **Cạm bẫy chết người:** Coi mỗi lời gọi `fetch()` như một thao tác độc lập mà không bật keep-alive hoặc cơ chế quản lý pool kết nối—buộc client hoặc máy chủ backend phải đóng mở bắt tay TCP/TLS liên tục trên từng chặng mạng.

## Bốn hành trình phổ biến của một Request [#bốn-hành-trình-phổ-biến-của-một-request]

<AtlasIllustration id="http-request-paths" />

Trước khi học thuộc các tầng giao thức, hãy tự hỏi: request bạn đang gỡ lỗi thuộc về hành trình nào trong 4 trường hợp trên?

### 1. Trúng Cache cục bộ của Client (Fresh cache hit) [#1-trúng-cache-cục-bộ-của-client-fresh-cache-hit]

```text
khởi tạo request
      ↓
cache của client tìm thấy bản ghi còn tươi mới (fresh)
      ↓
trả về dữ liệu đã lưu
```

Không có bất kỳ gói tin mạng nào cần phải rời khỏi máy khách.

### 2. Trượt Cache nhưng tái sử dụng kết nối sẵn có [#2-trượt-cache-nhưng-tái-sử-dụng-kết-nối-sẵn-có]

```text
khởi tạo request
      ↓
trượt cache (cache miss)
      ↓
tái sử dụng kết nối phù hợp đang mở
      ↓
gửi HTTP request
      ↓
nhận response
```

Giao dịch HTTP là mới, nhưng kết nối mạng bên dưới là kết nối cũ được tái sử dụng.

### 3. Chưa có kết nối phù hợp [#3-chưa-có-kết-nối-phù-hợp]

Trình duyệt cần thực hiện phân giải địa chỉ IP (DNS), thiết lập tầng truyền tải (TCP/QUIC) và đàm phán an ninh (TLS) trước khi có thể truyền tải request HTTP.

### 4. Trúng Cache tại máy chủ trung gian (CDN/Proxy) [#4-trúng-cache-tại-máy-chủ-trung-gian-cdnproxy]

```text
client trượt cache nội bộ
      ↓
gửi request qua mạng
      ↓
trúng cache tại máy chủ trung gian / Edge CDN
      ↓
nhận response

(bước xử lý tại backend gốc: hoàn toàn bị bỏ qua)
```

Chính sự đa dạng của các hành trình này giải thích vì sao &#x2A;*không tồn tại một chuỗi bắt buộc duy nhất "DNS → TCP → TLS → App"** cho mọi request HTTP.

## Tách bạch Ngữ nghĩa HTTP và Thiết lập Mạng [#tách-bạch-ngữ-nghĩa-http-và-thiết-lập-mạng]

```text
Ngữ nghĩa HTTP (HTTP Semantics)
Phương thức (Method) + Mục tiêu (Target) + Tiêu đề (Headers) + Body tùy chọn
                              ↓
Mã trạng thái (Status) + Tiêu đề (Headers) + Body phản hồi tùy chọn
```

Bao bọc xung quanh phiên trao đổi ngữ nghĩa đó là các tầng cache, tái sử dụng socket, máy chủ ủy quyền và máy chủ gốc. DNS, TCP và TLS ảnh hưởng lớn đến độ trễ, nhưng chúng không định nghĩa ngữ nghĩa của `GET`, `404 Not Found` hay `Cache-Control`.

<TermBox term="Origin">
  **Origin (Máy chủ gốc)** là thẩm quyền định danh được xác định bởi bộ ba: giao thức (`scheme`), tên miền (`host`) và cổng (`port`) của một URL.

  **Ý nghĩa thực tiễn:** Lưu lượng truy cập đến một origin vẫn có thể đi qua CDN, reverse proxy, API gateway hoặc load balancer trước khi chạm tới mã nguồn backend. Một request có đích đến là origin không chứng minh được thành phần nào đã thực sự sinh ra phản hồi.
</TermBox>

<TermBox term="Intermediary">
  **Intermediary (Thành phần trung gian)** là một thực thể đứng giữa client và origin, chẳng hạn như proxy, gateway, tường lửa WAF hoặc shared cache.

  **Ý nghĩa thực tiễn:** Thành phần trung gian có thể chuyển tiếp lưu lượng, định tuyến, biến đổi dữ liệu được phép hoặc tự động trả lời từ bộ nhớ cache mà không cần backend ứng dụng xử lý.
</TermBox>

## Bộ nhớ Cache trước khi ra Mạng [#bộ-nhớ-cache-trước-khi-ra-mạng]

<TermBox term="Fresh / stale cache entry">
  Một bản ghi cache được coi là &#x2A;*fresh (tươi mới)** khi các quy tắc caching cho phép tái sử dụng nó mà không cần gửi truy vấn kiểm tra lại với máy chủ. Một bản ghi là &#x2A;*stale (cũ/hết hạn)** khi nó đã vượt quá thời gian tươi mới cho phép.

  **Ý nghĩa thực tiễn:** Bản ghi fresh giúp triệt tiêu hoàn toàn độ trễ mạng, trong khi bản ghi stale sẽ kích hoạt cơ chế kiểm tra lại (revalidation) hoặc tải về bản ghi hoàn toàn mới.
</TermBox>

<TermBox term="Revalidation">
  **Revalidation (Xác thực lại)** là thao tác kiểm tra xem bản ghi đã lưu có còn hợp lệ để tiếp tục sử dụng hay không. Trường hợp phổ biến nhất là gửi một **conditional request** mang theo `If-None-Match` (ETag) hoặc `If-Modified-Since` và nhận về mã `304 Not Modified`.

  **Ý nghĩa thực tiễn:** Mã `304` chứng minh có một phiên trao đổi mạng đã diễn ra, nhưng phần thân dữ liệu (body) mà ứng dụng hiển thị lại được lấy trực tiếp từ bộ nhớ cục bộ mà không tốn băng thông tải lại.
</TermBox>

<AtlasIllustration id="http-cache-revalidation" />

## Tái sử dụng Kết nối và Các thế hệ HTTP [#tái-sử-dụng-kết-nối-và-các-thế-hệ-http]

<AtlasIllustration id="http-version-architecture" />

### HTTP/1.1, HTTP/2, và HTTP/3 [#http11-http2-và-http3]

* **HTTP/1.1:** Dạng văn bản thuần, hỗ trợ kết nối giữ lâu (`Keep-Alive`) nhưng bị giới hạn bởi hiện tượng nghẽn đầu hàng (Head-of-Line Blocking).
* **HTTP/2:** Giữ nguyên ngữ nghĩa, bổ sung đóng khung nhị phân (Binary Framing), nén tiêu đề (HPACK), và ghép nhiều stream đồng thời trên một kết nối TCP duy nhất.
* **HTTP/3:** Ánh xạ ngữ nghĩa HTTP lên giao thức truyền tải bảo mật QUIC chạy trên nền UDP, tích hợp sẵn bắt tay TLS 1.3 và loại bỏ hoàn toàn hiện tượng nghẽn đầu hàng ở tầng truyền tải.

<AtlasIllustration id="request-boundary-ownership" />

## Khám phá thực tế: Request Path Explorer [#khám-phá-thực-tế-request-path-explorer]

Hãy sử dụng công cụ mô phỏng dưới đây để đối chiếu từng chặng trong các kịch bản request phổ biến:

<HttpRequestPathExplorer />

## Tình huống thực tế trên Production [#tình-huống-thực-tế-trên-production]

### Tình huống: Hiện tượng "Mã 200 ma" và sự biến mất của log máy chủ [#tình-huống-hiện-tượng-mã-200-ma-và-sự-biến-mất-của-log-máy-chủ]

Một nhóm kỹ sư nhận được phản hồi khẩn từ người dùng: sau khi cập nhật thông tin cá nhân trên trang cá nhân, giao diện vẫn hiển thị thông tin cũ kỹ. Kỹ sư mở tab Network trên DevTools và thấy:

* Mã trạng thái: `200 OK`
* Dữ liệu trả về: Vẫn là thông tin cũ
* Thời gian: `3ms`

Nghi ngờ backend bị lỗi cache Redis hoặc cơ sở dữ liệu chưa ghi kịp, kỹ sư mất hơn hai giờ rà soát toàn bộ access log của hệ thống backend, nhưng kỳ lạ thay: **hoàn toàn không có bất kỳ request nào từ user đó được ghi nhận tại server**.

* **Hậu quả:** Tiêu tốn hàng giờ làm việc căng thẳng của đội ngũ frontend và backend do chẩn đoán sai ranh giới lỗi.
* **Nguyên nhân cốt lõi:** Kỹ sư đã bỏ qua cột Size/Transferred trong DevTools: nó hiển thị rõ ràng `(from disk cache)`! Request chưa từng rời khỏi máy của người dùng. Một response trước đó đã trả về `Cache-Control: public, max-age=3600`, ra lệnh cho trình duyệt tự phục vụ dữ liệu từ ổ cứng suốt 1 giờ mà không được hỏi lại server.
* **Cách khắc phục chuẩn:**
  1. Luôn nhìn cột Size/Transferred trước tiên: `(from disk cache)` hay `(from memory cache)` xác nhận request đã được giải quyết hoàn toàn tại client.
  2. Bổ sung `Cache-Control: private, no-cache` hoặc `no-store` đối với các API trả về dữ liệu định danh người dùng nhạy cảm để ép buộc trình duyệt luôn kiểm tra mạng.

## Bảng phân vùng ranh giới lỗi (Failure Boundaries) [#bảng-phân-vùng-ranh-giới-lỗi-failure-boundaries]

| Ranh giới lỗi               | Triệu chứng điển hình                               | Bằng chứng cần kiểm tra tiếp theo                            |
| --------------------------- | --------------------------------------------------- | ------------------------------------------------------------ |
| Phân giải tên miền (DNS)    | Không thể tìm thấy địa chỉ host (`ENOTFOUND`)       | Công cụ phân giải DNS, `nslookup`, `dig`, cấu hình mạng      |
| Thiết lập kết nối & Bảo mật | Lỗi bắt tay TCP hoặc chứng chỉ SSL/TLS không hợp lệ | Log chi tiết kết nối socket, kiểm tra hạn chứng chỉ TLS      |
| Trung gian HTTP (CDN/Proxy) | Nhận mã lỗi 502/504 từ Cloudflare, Nginx, Envoy     | Log của CDN, tiêu đề response `CF-Ray`, `X-Cache`, log proxy |
| Ứng dụng Backend gốc        | Lỗi logic ứng dụng (500 Internal Server Error)      | Log máy chủ backend, Application APM, Correlation ID         |
| Đọc Body dữ liệu            | Stream truyền tải bị ngắt quãng giữa chừng          | Log mạng, kiểm tra timeout đường truyền, client hủy request  |
| Xử lý tại Client            | Lỗi phân tích JSON, lỗi chặn CORS, fetch policy     | Console log của trình duyệt, cấu hình Fetch credentials/mode |

## Câu hỏi củng cố tư duy [#câu-hỏi-củng-cố-tư-duy]

Một lập trình viên ghi nhận thông tin request API trong DevTools như sau:

* Giao thức hiển thị là `HTTP/2`;
* Kết nối hiển thị tái sử dụng ID kết nối cũ;
* Trả về mã `304 Not Modified`;
* Dịch vụ backend không có log xử lý dữ liệu body;
* Giao diện web cập nhật hiển thị dữ liệu đầy đủ.

<details>
  <summary>
    Xem giải thích chi tiết
  </summary>

  1. **Bằng chứng nào chứng minh có trao đổi qua mạng?**\
     Mã `304 Not Modified` chỉ có thể được sinh ra khi một request có điều kiện (chứa `If-None-Match` hoặc `If-Modified-Since`) được gửi thành công qua mạng và nhận phản hồi từ server/CDN. Nếu chỉ trúng cache nội bộ không qua mạng, DevTools sẽ báo `200 OK (from disk cache)`.
  2. **Những giai đoạn thiết lập kết nối nào đã được bỏ qua?**\
     Vì kết nối HTTP/2 cũ được tái sử dụng, request này hoàn toàn không tốn thời gian cho việc phân giải DNS, bắt tay 3 bước TCP (TCP Handshake) và đàm phán bảo mật TLS.
  3. **Tại sao mã 304 không gửi lại dữ liệu thân (body)?**\
     Bản chất của `304` là thông báo cho client biết rằng bản sao trong cache của họ vẫn còn nguyên giá trị. Response chỉ chứa tiêu đề (headers) và phần body hoàn toàn rỗng (0 bytes truyền tải). Trình duyệt tự đọc thân dữ liệu từ cache cục bộ để vẽ giao diện.
  4. **Cần bằng chứng gì để khẳng định request đã đến backend thay vì dừng lại ở CDN?**\
     Cần có log truy vết phân tán (Distributed Trace ID) hoặc access log khớp thời gian từ container backend. Nếu CDN edge đã lưu bản đồ ETag, chính CDN có quyền trả ngay mã `304` mà không cần chuyển tiếp request về backend gốc.
</details>

## Nguyên tắc cốt lõi dành cho Kỹ sư [#nguyên-tắc-cốt-lõi-dành-cho-kỹ-sư]

> Khi gỡ lỗi hoặc viết mã tương tác qua HTTP, không bao giờ giả định một chuỗi tuần tự cứng nhắc `DNS -> TCP -> TLS -> Backend`. Hãy xác định xem request có bị chặn bởi cache không, có tái sử dụng kết nối không, phiên bản HTTP nào đang phục vụ, thành phần nào thực sự sinh ra mã trạng thái, và lỗi gặp phải thuộc tầng mạng, tầng giao thức hay tầng nghiệp vụ.

Checklist kiểm tra khi rà soát luồng HTTP:

* [ ] **Kiểm tra tầng cache:** Request có thực sự đi ra mạng hay được giải quyết ngay bởi `disk cache` / `memory cache`?
* [ ] **Trạng thái tái sử dụng kết nối:** Request này có tái sử dụng socket/TLS session cũ hay phải chịu chi phí bắt tay mới từ đầu?
* [ ] **Nhận diện thế hệ giao thức:** Giao dịch đang chạy trên HTTP/1.1 (tuần tự), HTTP/2 (ghép kênh TCP) hay HTTP/3 (QUIC UDP)?
* [ ] **Xác định ranh giới phản hồi:** Mã trạng thái và headers được sinh ra từ Edge CDN, API Gateway, WAF hay chính mã nguồn backend?
* [ ] **Cấu hình xác thực lại (Revalidation):** Các tiêu đề `ETag`, `Last-Modified`, và `Cache-Control` đã được thiết lập chuẩn để tận dụng mã phản hồi nhẹ `304 Not Modified` chưa?
* [ ] **Phân tách tầng lỗi:** Sự cố đang gặp phải là đứt kết nối mạng vật lý, lỗi giao thức cổng trung gian (502/504), hay lỗi nghiệp vụ được bọc trong mã 200?
* [ ] **Quy chuẩn Retry an toàn:** Cơ chế gửi lại (retry) có được giới hạn nghiêm ngặt cho các thao tác an toàn (idempotent) hoặc được bảo vệ bằng Idempotency Key không?

## Nguồn tham khảo chuẩn mực [#nguồn-tham-khảo-chuẩn-mực]

* [RFC 9110 — HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110.html)
* [RFC 9111 — HTTP Caching](https://www.rfc-editor.org/rfc/rfc9111.html)
* [RFC 9113 — HTTP/2](https://www.rfc-editor.org/rfc/rfc9113.html)
* [RFC 9114 — HTTP/3](https://www.rfc-editor.org/rfc/rfc9114.html)
* [RFC 9001 — Using TLS to Secure QUIC](https://www.rfc-editor.org/rfc/rfc9001.html)
* [WHATWG Fetch Living Standard](https://fetch.spec.whatwg.org/)
