How Browsers Work · Part 9 — Networking & HTTP Caching
The network stack from the browser's side: the resource fetch lifecycle, HTTP caching (Cache-Control, ETag, the disk/memory cache), connection reuse, HTTP/2 and HTTP/3, and request prioritization.
Khi trang gọi fetch() hoặc nạp <script src="...">, renderer không mở socket trực tiếp. Như Phần 1, network service do browser quản lý thực hiện DNS, connection, TLS, HTTP, cookie và HTTP cache qua interface IPC được cấp. Service thường ở utility process riêng; sandbox/capability cụ thể tùy platform.
Phần này đi từ thông điệp IPC đến response vào JavaScript: caching, kết nối, phiên bản HTTP, và ưu tiên. Làm sai những thứ này khiến code nhanh chạy trên mạng chậm — hoặc code nhanh mà vẫn cảm giác chậm.
1. Vòng đời fetch từ góc nhìn renderer
Mọi yêu cầu tài nguyên — navigation, subresource, fetch(), XHR — đi theo đường gần giống nhau:
Renderer (main thread or worker)
│
│ 1. Build request (URL, method, headers, body, credentials, priority)
▼
Browser-managed fetch path
│
│ 2. Policy checks; dispatch fetch event if a Service Worker controls it
│ 3. SW may return Cache API response or call fetch()
│ 4. Network fetch consults HTTP cache, then DNS/connection if needed
│ 5. Send HTTP request on an H1/H2/H3 stream
│ 6. Receive/filter response; update HTTP cache when allowed
▼
Renderer
│
│ 7. Deliver body to caller (parser, fetch promise, etc.)
▼
Your code
Đây là mental model, không phải thứ tự tuyệt đối của mọi cache nội bộ: Chrome còn có renderer memory cache có thể short-circuit một số request. Fetch/CORS, CSP, mixed-content, cookie và response filtering được enforce ở nhiều component browser/renderer phối hợp trước khi JS được đọc response. Chi tiết bảo mật ở Phần 11.
Trong Network DevTools, cột Size cho biết byte từ đâu:
(memory cache),(disk cache), hoặc kích thước thực truyền.
2. HTTP cache và các cache nội bộ
HTTP định nghĩa semantics reuse/validation, không bắt user agent phải có đúng hai cache. Chromium có HTTP cache phía network service và Blink in-memory resource cache; browser còn có image decode cache, preload cache và Cache API — chúng không đồng nghĩa nhau.
| Nhãn/khái niệm | Ý nghĩa thực dụng |
|---|---|
(memory cache) trong DevTools | Response lấy từ cache volatile/internal; scope và lifetime là implementation detail |
(disk cache) | HTTP response lấy từ persistent backend nếu profile/policy cho phép; vẫn có thể bị evict |
| Cache API | Kho do app điều khiển qua caches, tách khỏi HTTP cache tự động |
Fresh hit thường tránh liên hệ origin. Entry stale có thể revalidate và nhận 304; Service Worker có thể trả response khác trước khi network fetch, hoặc gọi fetch() rồi vẫn đi qua HTTP cache. Bài Service worker caching and HTTP caching minh họa các layer và cũng lưu ý memory cache khác nhau theo browser.
Không phải mọi thứ đều vào HTTP cache. Response có Cache-Control: no-store không được HTTP cache lưu; private không vào shared cache (CDN) nhưng private browser cache vẫn có thể lưu. Cache API là kho imperative riêng: code có thể put() response theo logic của mình, nên đừng nhầm header HTTP với policy Cache API.
3. Cache-Control, độ tươi, và revalidation
Server báo trình duyệt response dùng bao lâu không cần kiểm tra lại qua Cache-Control:
| Chỉ thị | Ý nghĩa |
|---|---|
max-age=3600 | Freshness lifetime 3600 giây; current age tính cả thời gian qua cache/proxy |
no-cache | Có thể lưu, nhưng phải revalidate với origin trước mỗi lần dùng |
no-store | Không lưu |
immutable | Nội dung không đổi trong freshness lifetime — tránh revalidation khi còn fresh |
public / private | OK cho CDN dùng chung vs chỉ trình duyệt |
Tươi vs cũ
Khi tươi, cache có thể reuse response không round-trip. Khi stale, nó thường phải validate trước khi reuse, trừ khi directive như stale-while-revalidate/stale-if-error hay quy tắc disconnected cho phép phục vụ stale. Nếu server không gửi freshness rõ, cache có thể dùng heuristic theo RFC 9111.
Revalidation với ETag và Last-Modified
Khi revalidate, trình duyệt gửi header có điều kiện:
- Response có
ETag: "abc123"→ request gửiIf-None-Match: "abc123". - Response có
Last-Modified→ request có thể gửiIf-Modified-Since.
Nếu không đổi, server trả 304 Not Modified body rỗng — trình duyệt tái dùng byte cache. Tiết kiệm băng thông nhưng vẫn tốn một RTT (độ trễ).
# First request
GET /app.js HTTP/1.1
HTTP/1.1 200 OK
Cache-Control: public, max-age=31536000, immutable
ETag: "v3-app"
Content-Length: 84200
(... body ...)
# Later request after max-age expires (or no-cache)
GET /app.js HTTP/1.1
If-None-Match: "v3-app"
HTTP/1.1 304 Not Modified
ETag: "v3-app"
Với asset có fingerprint (app.a1b2c3.js), max-age dài + immutable là lý tưởng — URL đổi khi nội dung đổi. Với HTML, no-cache hoặc max-age ngắn an toàn hơn để user có shell page mới.
4. Quản lý kết nối và chi phí handshake
Một request lạnh có thể trả chi phí thiết lập trước byte đầu:
Cold request to https://cdn.example.com
DNS lookup (nếu miss)
TCP handshake 1 RTT cho H1/H2
TLS 1.3 full handshake thường 1 RTT trên TCP
HTTP request + server work + first response byte
Hoặc: QUIC + TLS 1.3 cho HTTP/3 (không có TCP riêng)
Kết nối persistent tái dùng transport/TLS cho nhiều request và có thể coalesce cho origin khác khi đủ điều kiện. DNS, resumption, server think time và congestion đều ảnh hưởng TTFB; số mili giây cố định không có ý nghĩa nếu chưa biết RTT/workload.
Hạn chế HTTP/1.1
Browser phổ biến không dùng HTTP/1.1 pipelining, nên thường chỉ có một response active theo thứ tự trên mỗi connection và mở một pool nhỏ connection song song. Giới hạn (hay gặp khoảng 6) là implementation detail, không phải contract. Request/response chậm trên một connection giữ request sau của connection đó.
5. HTTP/2 và HTTP/3: multiplexing và hơn nữa
HTTP/2
HTTP/2 chạy nhiều stream trên một kết nối. Request và response chia frame xen kẽ trên dây — không head-of-line blocking cấp HTTP trên một kết nối. HPACK nén header, giảm overhead trên trang nhiều asset.
HTTP/1.1 (6 connections, 1 req each) HTTP/2 (1 connection, many streams)
Conn 1: [====JS====] Conn 1: [=JS=][=CSS=][=img=][=font=]
Conn 2: [==CSS==] ↑ interleaved frames
Conn 3: [img................]
...
Server Push vẫn có trong protocol nhưng Chrome đã bỏ HTTP/2 Push; nhiều deployment ưu tiên preload/103 Early Hints vì cache và ưu tiên dễ kiểm soát hơn. Đừng bật push chỉ vì HTTP/2 hỗ trợ nó.
HTTP/3 (QUIC)
HTTP/3 chạy trên QUIC (dựa UDP) thay TCP. Lợi ích chính:
- 0-RTT early data có thể dùng khi resume và server chấp nhận (phải xử lý replay risk).
- Stream độc lập — mất gói trên một stream không chặn stream khác (sửa head-of-line blocking tầng transport mà HTTP/2 vẫn có trên TCP).
- Migration kết nối — sống sót đổi IP/mạng (handoff mobile).
HTTP/3 semantics được chuẩn hóa trong RFC 9114. Browser tự negotiate protocol mà server/CDN quảng bá và có thể fallback về TCP khi QUIC/UDP không dùng được; application code không nên giả định một version cụ thể.
6. Ưu tiên request và thứ tự tài nguyên
Không phải mọi byte đều gấp như nhau. Browser gán priority heuristic từ destination, parser state, viewport, async/defer, preload và hint. Giá trị đổi theo engine/phiên bản và còn có thể được boost sau discovery:
| Tín hiệu | Hệ quả thường gặp |
|---|---|
| Main document, blocking CSS/script | Ưu tiên cao vì mở khóa parsing/rendering |
| Ảnh được xác định trong viewport/LCP candidate | Có thể được boost sau layout hoặc nhờ fetchpriority="high" |
| Font | Chỉ được discover khi CSSOM biết rule được dùng; preload có thể kéo discovery sớm |
prefetch, lazy resource, fetchpriority="low" | Thường thấp hơn critical resource |
Trong HTTP/2 và HTTP/3, browser có thể truyền tín hiệu extensible priority để server/CDN biết stream nào nên lên lịch trước. Server có thể bỏ qua hint; asset lớn vẫn tranh congestion window/băng thông với CSS — lý do thêm để resize và lazy-load ảnh.
Thuộc tính fetchpriority chỉ là hint tăng/giảm so với heuristic, không ép thứ tự và không biến ảnh không cần thiết thành critical:
<img src="hero.webp" fetchpriority="high" alt="..." />
<img src="below-fold.webp" loading="lazy" fetchpriority="low" alt="..." />
7. Resource hint: preconnect, preload, prefetch
Resource hint là khai báo <link rel="..."> trong HTML (hoặc header Link) báo trình duyệt bắt đầu sớm:
| Hint | Khi dùng |
|---|---|
dns-prefetch | Resolve DNS cho origin sắp cần (rẻ) |
preconnect | DNS + thiết lập transport/TLS tới origin sắp gọi (font, API, CDN) |
preload | Fetch sớm tài nguyên trang hiện tại quan trọng |
prefetch | Fetch ưu tiên thấp tài nguyên navigation tiếp theo có thể |
modulepreload | Fetch/parse/compile module sớm; dependency graph có thể được fetch thêm tùy implementation |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin />
<link rel="prefetch" href="/dashboard" />
Dùng hint có chọn lọc — mỗi preload tranh băng thông với tài nguyên thật sự quan trọng. Preload quá mức làm hại LCP.
8. Service Worker: proxy mạng lập trình được (xem trước)
Service Worker là script nằm giữa page và network fetch, xử lý sự kiện fetch khi đang control client. Nó có thể thực hiện chiến lược HTTP cache tự động không biểu đạt được: offline fallback hay stale-while-revalidate tùy chỉnh. Background Sync là capability worker khác, không phải cache strategy.
fetch() in page
│
▼
Service Worker (if registered & controlling)
│ cache.match() → hit? return cached Response
│ miss? fetch(network) → maybe cache.put()
▼
HTTP cache → network
Service Worker cũng dùng Cache API (Phần 10) — kho Request/Response lập trình được, tách khỏi HTTP cache tự động. Mạnh và dễ làm sai (bug cũ mãi mãi, cạn quota cache). Phần 10 đi sâu cơ chế lưu trữ và lifecycle SW.
Tóm tắt
- Network service xử lý mọi I/O; renderer yêu cầu tài nguyên qua IPC.
- HTTP cache có backend memory/disk và Blink còn resource cache nội bộ; scope/lifetime không được chuẩn hóa. Cache API là lớp app điều khiển riêng.
Cache-Control,ETag, vàLast-Modifiedđiều khiển độ tươi và revalidation 304.- Tái dùng kết nối; HTTP/2 multiplex stream nhưng cùng chịu TCP loss, còn HTTP/3 trên QUIC cô lập packet loss giữa stream.
- Trình duyệt ưu tiên tài nguyên quan trọng; dùng
fetchpriorityvà resource hint (preconnect,preload,prefetch) có chủ đích. - Service Worker thêm lớp lập trình trên HTTP cache — chi tiết ở Phần 10.
Phần tiếp theo: Storage & the platform — cookie, Web Storage, IndexedDB, Cache API, cùng scope và partition key khác nhau của chúng.