Mỗi lần một trang web tải xong, trình duyệt đã giữ sẵn trong tay ảnh, file CSS, JavaScript và font của trang đó. Câu hỏi quyết định tốc độ lần ghé sau là: trình duyệt có được phép dùng lại những file ấy không, hay phải tải lại từ đầu? Quyền đó nằm gọn trong mấy dòng header máy chủ gửi về cùng từng tài nguyên.
Vì sao cache file tĩnh giúp khách quay lại nhanh hơn
File tĩnh (static asset — tài nguyên không đổi nội dung như logo, CSS bản dựng, font) chiếm phần lớn dung lượng một trang. Nếu trình duyệt phải tải lại toàn bộ ở mỗi lần ghé, khách quay lại vẫn chờ y như khách mới. Khi máy chủ nói rõ “file này giữ được một năm”, trình duyệt đọc thẳng từ ổ đĩa máy khách, gần như tức thì, không một gói tin nào ra mạng.
Lợi ích dồn vào hai chỗ. Thứ nhất, điểm Core Web Vitals ở lượt xem lặp lại tốt hơn hẳn vì ảnh nền lớn (ứng viên LCP) đã nằm sẵn trong cache. Thứ hai, máy chủ nhẹ tải vì không phải phục vụ lại cùng một file cho cùng một người. Đây là tối ưu rẻ nhất trong nghề: chỉ là cấu hình header, không đụng vào code trang.

Cache-Control: header chỉ huy chính
Trong các browser cache headers, Cache-Control là header quan trọng nhất và được ưu tiên hơn mọi header cũ. Nó gồm nhiều directive (chỉ thị) ghép lại, ngăn bởi dấu phẩy.
max-age=N— file còn “tươi” (fresh) trong N giây kể từ lúc máy chủ sinh ra.max-age=31536000là một năm.public— cho phép cả cache dùng chung (CDN, proxy) lưu, không chỉ trình duyệt của một người.private— chỉ trình duyệt của khách được lưu, CDN không được. Dùng cho nội dung cá nhân hoá.immutable— báo file sẽ không bao giờ đổi khi còn tươi, nên trình duyệt khỏi gửi cả request kiểm tra lại. Đây là khác biệt quan trọng so với chỉ dùngmax-age.no-cache— gây hiểu nhầm nhiều nhất: KHÔNG có nghĩa “đừng lưu”. Nó nghĩa là “lưu được, nhưng mỗi lần dùng phải hỏi lại máy chủ xem còn mới không”.no-store— mới thật sự cấm lưu bất cứ đâu. Dùng cho dữ liệu nhạy cảm (thông tin tài khoản, trang sau đăng nhập).
Cặp đôi max-age dài cộng immutable mạnh khi đi kèm cache-busting (chống cache cũ — gắn mã băm vào tên file, ví dụ style.a1b2c3.css). Vì tên file đổi mỗi lần nội dung đổi, không bao giờ có chuyện khách kẹt bản cũ, nên cứ cache một năm thoải mái.
ETag và Expires: hai header phụ trợ
ETag (Entity Tag — dấu vân tay phiên bản file) là một chuỗi máy chủ gán cho mỗi phiên bản tài nguyên. Khi cache hết hạn hoặc gặp no-cache, trình duyệt gửi ETag cũ kèm header If-None-Match. Nếu file chưa đổi, máy chủ trả về 304 Not Modified với thân rỗng thay vì gửi lại cả file. Khách vẫn tiết kiệm băng thông, dù không nhanh bằng dùng thẳng bản cache.
Expires là header thời HTTP/1.0, ghi một mốc ngày giờ tuyệt đối file hết hạn. Nó cũ và bị Cache-Control: max-age ghi đè khi cả hai cùng có. Bạn không cần đặt Expires nếu đã có Cache-Control; chỉ giữ lại để tương thích ngược với hạ tầng rất cũ.
| Header | Vai trò | Ưu tiên |
|---|---|---|
| Cache-Control | Quy tắc cache đầy đủ, hiện đại | Cao nhất |
| ETag | Kiểm tra file đổi chưa, trả 304 nếu chưa | Khi cần revalidate |
| Expires | Mốc hết hạn kiểu cũ | Bị max-age ghi đè |
Cách đặt header trên server, .htaccess và CDN
Apache (.htaccess hoặc cấu hình vhost)
<IfModule mod_headers.c>
<FilesMatch ".(css|js|woff2|webp|avif|png|jpg|svg)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
<FilesMatch ".(html)$">
Header set Cache-Control "no-cache"
</FilesMatch>
</IfModule>Nginx
location ~* .(css|js|woff2|webp|avif|png|jpg|svg)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}Quy tắc chia hai nhóm rất rõ. File tĩnh có gắn mã băm thì cache cực dài kèm immutable. Riêng file HTML thì để no-cache để trình duyệt luôn hỏi lại máy chủ, nhờ vậy sau mỗi lần cập nhật khách nhận đúng tên file CSS/JS mới. Đặt nhầm HTML thành cache dài là lý do phổ biến khiến khách thấy bản cũ dai dẳng.
Trên CDN, header cấu hình ngay tại lớp biên (edge). Với Cloudflare, bạn dùng Cache Rules để ép Edge Cache TTL và Browser Cache TTL cho từng nhóm đuôi file; xem cách dựng trong bài audit hiệu năng website và bài cấu hình CDN ở cuối. Web22 vận hành web22.dev trên LiteSpeed cộng Cloudflare, đặt file tĩnh cache một năm với immutable và để HTML revalidate, nên lượt xem lặp lại của khách quen tải gần như không chạm máy chủ.
Browser cache khác server cache ở đâu
Hai khái niệm hay bị gộp nhưng nằm ở hai đầu khác nhau của đường truyền. Browser cache là bản sao file nằm trên máy của chính khách, header này điều khiển. Server cache (bộ nhớ đệm phía máy chủ) là việc máy chủ dựng sẵn trang HTML hoặc lưu kết quả truy vấn để khỏi tính lại cho mọi khách. Một bên giảm lượt tải lặp cho từng người; một bên giảm công xử lý của máy chủ cho tất cả mọi người. Cả hai bổ trợ nhau, không thay thế nhau.

Câu hỏi thường gặp
Đặt max-age một năm có sợ khách kẹt bản cũ không?
Không, nếu file tĩnh có gắn mã băm vào tên (cache-busting). Khi nội dung đổi, tên file đổi theo nên URL mới, trình duyệt buộc tải lại. File HTML thì để no-cache nên luôn cập nhật.
Nên dùng ETag hay max-age dài?
Ưu tiên max-age dài kèm immutable cho file tĩnh vì tiết kiệm cả request kiểm tra. ETag chỉ phát huy khi file buộc phải revalidate, ví dụ HTML đặt no-cache.
no-cache có làm web chậm hơn không?
Không cấm lưu, chỉ thêm một request kiểm tra nhẹ. Nếu file chưa đổi, máy chủ trả 304 rỗng rất nhanh. Đặt cho HTML là hợp lý; đặt cho ảnh hay CSS thì phí.
