Cache là một trong những “đòn bẩy” quan trọng nhất để tăng hiệu năng cho web. Nhưng nếu dùng sai, nó cũng là nguồn gốc của rất nhiều bug khó chịu: dữ liệu cũ, UI không cập nhật, hoặc người dùng thấy nội dung khác nhau.
Trong bài này, mình sẽ đi từ hiểu bản chất Cache-Control, đến cách nó hoạt động thực tế, và cuối cùng là best practices để áp dụng trong production.
1. Cache-Control là gì?
Cache-Control là HTTP header dùng để chỉ định:
**Cách một response được lưu cache và khi nào được phép sử dụng lại**
Nó được gửi từ server → client (browser, CDN, proxy).
Ví dụ:
Cache-Control: public, max-age=0, must-revalidate2. Các directive quan trọng
2.1 public vs private
public- Cho phép mọi cache lưu lại
- Bao gồm: browser, CDN, proxy
private- Chỉ cache ở browser của user
- Không cache ở CDN
👉 Quy tắc:
- Static assets →
public - Data cá nhân →
private
2.2 max-age
Cache-Control: max-age=3600Response được coi là fresh trong 3600 giây (1 giờ)
Trong thời gian này:
- Không cần gọi server
- Cache được dùng trực tiếp
👉 Đây là cách tối ưu performance mạnh nhất.
2.3 no-cache
Nghe tên dễ hiểu nhầm.
Cache-Control: no-cacheKHÔNG có nghĩa là “không cache”.
Nó có nghĩa:
“Có thể cache, nhưng phải kiểm tra với server trước khi dùng”
2.4 no-store
Cache-Control: no-storeKhông lưu cache ở bất kỳ đâu
Dùng cho:
- Thông tin nhạy cảm
- Banking, auth
2.5 must-revalidate
Cache-Control: must-revalidateKhi cache hết hạn:
- BẮT BUỘC phải hỏi server
- Không được dùng cache cũ
3. Hiểu sâu qua ví dụ thực tế
Cache-Control: public, max-age=0, must-revalidateÝ nghĩa:
public→ cache được lưu ở mọi nơimax-age=0→ hết hạn ngay lập tứcmust-revalidate→ phải check server trước khi dùng
👉 Tổng hợp lại:
“Có thể cache, nhưng mỗi lần dùng phải hỏi server xem có mới không”
4. Cơ chế revalidation (cực kỳ quan trọng)
Khi cache cần kiểm tra lại, nó dùng:
ETag
Server trả:
ETag: "abc123"Client gửi lại:
If-None-Match: "abc123"Nếu không thay đổi:
HTTP 304 Not Modified→ Không cần gửi lại body → tiết kiệm bandwidth
Last-Modified
Tương tự:
Last-Modified: Tue, 26 Mar 2026 10:00:00 GMTClient:
If-Modified-Since: ...5. Các chiến lược cache phổ biến
5.1 Static assets (JS, CSS, images)
Cache-Control: public, max-age=31536000, immutableCache 1 năm
Không bao giờ revalidate
Dùng kèm:
- filename hash (
app.abc123.js)
- filename hash (
👉 Best performance
5.2 HTML (dynamic content)
Cache-Control: public, max-age=0, must-revalidate- Luôn check server
- Nhưng vẫn tận dụng 304
👉 Phù hợp với:
- SSR
- Dashboard
- Content thay đổi thường xuyên
5.3 API responses
Trường hợp real-time
Cache-Control: no-storeTrường hợp có thể cache nhẹ
Cache-Control: public, max-age=60👉 Ví dụ:
- List sản phẩm
- Data không thay đổi liên tục
5.4 User-specific data
Cache-Control: private, max-age=300- Cache ở browser
- Không leak qua CDN
6. Những sai lầm phổ biến
1. Dùng no-cache khi muốn “không cache”
→ Sai
→ Phải dùng no-store
2. Không version static assets
→ Người dùng bị kẹt với file cũ
👉 Luôn dùng:
app.[hash].js3. Cache API mà không nghĩ về consistency
→ User thấy dữ liệu cũ
→ Bug rất khó debug
4. Không hiểu CDN cache
CDN có thể cache ngay cả khi bạn không để ý
Cần rõ ràng:
publicvsprivate
7. Best Practices (tóm tắt)
🔹 Static assets
public, max-age=31536000, immutable🔹 HTML
public, max-age=0, must-revalidate🔹 API
- Real-time →
no-store - Semi-static →
max-age=60
🔹 Sensitive data
no-store🔹 Luôn dùng versioning
- Hash filename
- Tránh cache bug
Kết
Cache-Control là một trong những thứ:
- Nhìn thì đơn giản
- Nhưng ảnh hưởng cực lớn đến hệ thống
Hiểu đúng nó giúp bạn:
- Giảm load server
- Tăng tốc độ app
- Tránh hàng loạt bug khó chịu