Một màn hình kiểm tra trạng thái cứ 15 giây có thể tải lại cùng một tài liệu JSON hàng trăm lần dù nội dung chưa đổi. Nén response giúp giảm kích thước mỗi lần tải; cache validation tiến thêm một bước: server xác nhận bản đã lưu còn dùng được để tránh gửi lại toàn bộ body.
Bài viết dùng ví dụ một API đọc trạng thái báo cáo. Các URL và header dưới đây là minh họa thiết kế, không phải endpoint đang được triển khai trên website này. Mục tiêu là hiểu khi nào dùng ETag, cấu hình quyền lưu cache và xác minh hành vi qua HTTP thực tế.
1. Tách thời gian được dùng cache khỏi việc kiểm tra thay đổi
Freshness trả lời “bản lưu còn đủ mới để sử dụng trực tiếp không?”. Validation trả lời “bản lưu đã cần kiểm tra có còn khớp với server không?”. Cache-Control điều khiển chính sách; ETag cung cấp một dấu nhận diện cho representation được trả về.
Với response còn fresh theo chính sách, cache có thể phục vụ mà không gọi origin. Khi cần xác nhận lại, client gửi validator. Vì thế 304 vẫn cần một lượt request–response; nó không loại bỏ độ trễ mạng như một cache hit còn fresh.
2. Đọc luồng 200 → conditional GET → 304
Lần đầu, người dùng được cấp quyền đọc báo cáo và nhận dữ liệu cùng validator. Ví dụ dùng weak ETag vì chỉ cam kết cùng trạng thái nghiệp vụ, không cam kết mọi byte JSON giống nhau:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: private, no-cache
ETag: W/"report-42-rev-7-json-v1"
{"id":42,"status":"ready","rows":1200}
Lần kiểm tra tiếp theo, client giữ nguyên dấu ngoặc kép và tiền tố của tag:
GET /api/reports/42 HTTP/1.1
If-None-Match: W/"report-42-rev-7-json-v1"
Nếu bản hiện tại vẫn tương đương, server trả 304 không có body. Response cần mang các metadata áp dụng để cache cập nhật bản lưu, chẳng hạn ETag và Cache-Control; nếu response 200 có Vary thì phải giữ đúng thông tin tương ứng khi trả 304.
HTTP/1.1 304 Not Modified
Cache-Control: private, no-cache
ETag: W/"report-42-rev-7-json-v1"
Khi báo cáo thay đổi, server trả 200 cùng body mới và tag mới. Theo RFC 9110, mục If-None-Match, phép so sánh cho header này là weak comparison. Với GET hoặc HEAD, điều kiện không thỏa dẫn tới 304; không áp dụng máy móc mã này cho các phương thức ghi.
3. Chọn Cache-Control trước khi bật ETag
| Chính sách minh họa | Ý nghĩa vận hành | Trường hợp cân nhắc |
|---|---|---|
private, no-cache | Private cache được lưu nhưng phải xác nhận trước khi dùng lại; shared cache không được lưu | Dữ liệu riêng cần cập nhật khi mở lại |
public, max-age=60 | Cho phép cache công khai và thời gian freshness 60 giây | Danh mục công khai chịu được độ trễ cập nhật |
no-store | Yêu cầu cache không lưu response này | Response chứa bí mật hoặc chính sách cấm lưu |
no-cache không có nghĩa là cấm lưu. Ngược lại, gắn ETag vào response no-store không tạo ra lợi ích tái sử dụng thông thường vì cache được yêu cầu không giữ bản đó. Đây là hai quyết định khác nhau, được giải thích trong tài liệu Cache-Control của MDN.
Private cache vẫn là nơi lưu dữ liệu. Với dữ liệu nhạy cảm trên máy dùng chung, cần đánh giá cả chính sách lưu phía ứng dụng và đăng xuất; private không tự biến nội dung thành an toàn trong mọi hoàn cảnh.
4. Tag phải đại diện cho đúng biến thể
Một URL có thể trả tiếng Việt hoặc tiếng Anh, dữ liệu tóm tắt hoặc chi tiết, định dạng mới hoặc cũ. Validator cần thay đổi khi representation thay đổi theo ý nghĩa đã cam kết. Revision của một hàng database không đủ nếu response còn chứa tên tác giả lấy từ bảng khác mà revision đó không phản ánh.
Với strong ETag, cùng tag phải bảo đảm representation data giống từng byte. Hash trên đúng dữ liệu xuất ra là một cách tiếp cận, nhưng có thể tốn CPU và đòi hỏi phối hợp với tầng nén. Nếu chỉ chứng minh được tương đương ngữ nghĩa, dùng weak ETag có tiền tố W/. Xem cú pháp và ví dụ ETag của MDN.
Khi response thay đổi theo Accept-Language, cấu hình Vary tương ứng; khi có nhiều content coding, kiểm tra cả Vary: Accept-Encoding và cách tầng proxy quản lý validator. Vary giúp phân biệt cache key theo header, không thay thế kiểm soát quyền truy cập. Không đưa token hoặc thông tin nhận diện nhạy cảm vào tag.
5. Kiểm tra quyền trước khi quyết định 304
Người từng được đọc báo cáo có thể đã bị thu hồi quyền. Server phải xử lý xác thực và phân quyền theo trạng thái hiện tại trước khi dùng conditional header để kết luận nội dung còn hợp lệ. Không trả 304 chỉ vì tag khớp rồi vô tình cho client tiếp tục dùng dữ liệu của quyền truy cập đã hết.
Luồng xử lý ở mức thiết kế:
- Xác thực request và xác định user/tenant.
- Tìm tài nguyên trong scope được phép; trả lỗi phù hợp khi không được đọc.
- Chọn representation và tính validator từ một snapshot nhất quán.
- Dùng parser conditional request của framework để xử lý danh sách tag, wildcard và weak comparison.
- Trả 304 nếu điều kiện phù hợp, hoặc 200 với body và validator khớp nhau.
Không tự so sánh nguyên chuỗi header bằng phép bằng đơn giản: If-None-Match có thể chứa nhiều tag hoặc dấu *. Nếu body và tag được lấy ở hai thời điểm khác nhau, response còn có thể mang validator của phiên bản khác. Cần thiết kế snapshot hoặc revision nhất quán trước khi tối ưu.
6. Đo băng thông và chi phí origin riêng biệt
Giả sử body có kích thước 200 KB và 900 lần kiểm tra không có thay đổi. Phần body tránh truyền xấp xỉ 180 MB theo đơn vị thập phân, chưa tính header, nén và các request thật sự có cập nhật. Đây là phép tính minh họa, không phải kết quả benchmark.
Nếu server vẫn chạy mọi JOIN và serialize toàn bộ JSON rồi mới hash, 304 chủ yếu tiết kiệm đường truyền. Muốn giảm tải database, cần validator rẻ nhưng phản ánh đầy đủ phụ thuộc, hoặc cache representation phía server. Theo dõi riêng số byte truyền, tỷ lệ 304, thời gian xử lý và số query; tỷ lệ 304 cao không chứng minh origin đã nhẹ.
7. Checklist kiểm thử trước khi áp dụng
- GET đầu trả 200, body hợp lệ, ETag và Cache-Control đúng chính sách.
- GET lại với tag hiện tại trả 304, không kèm body.
- Sửa một dữ liệu được hiển thị rồi gửi tag cũ: nhận 200 với dữ liệu và tag mới.
- Kiểm tra tag yếu, danh sách nhiều tag và wildcard qua thư viện HTTP đang dùng.
- Hai user hoặc tenant không nhận nội dung của nhau; quyền bị thu hồi không nhận 304 thay cho lỗi truy cập.
- Kiểm tra từng ngôn ngữ, bộ lọc, dạng nén và response đi qua CDN/proxy thực tế.
- Với client tự quản cache, xử lý 304 trước khi parse JSON; với browser HTTP cache, kiểm tra cả Network panel vì tầng cache có thể hợp nhất bản lưu thành response dùng được.
curl có thể gửi conditional header nhưng không tự cung cấp một kho body đã lưu cho ứng dụng. Vì vậy hãy kiểm tra cả header trên đường truyền và cách client khôi phục dữ liệu. Những hướng dẫn ở đây cần được kiểm thử trên stack thực tế trước khi áp dụng cho hệ thống quan trọng.
Tài liệu tham khảo
- RFC 9110 — HTTP Semantics: validator, điều kiện request và 304.
- RFC 9111 — HTTP Caching: freshness, validation và cập nhật response được lưu.
- MDN — ETag và MDN — Cache-Control.
Bắt đầu bằng một endpoint đọc có body lớn và ít thay đổi, chọn chính sách lưu phù hợp rồi đo trước/sau. ETag phát huy giá trị khi validator đúng, cache đúng phạm vi và client thực sự tái sử dụng được dữ liệu.




Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.