Cache-Control Header
Header HTTP định nghĩa cách và thời gian tài nguyên được lưu tạm (client, CDN, proxy).
Cache-Control Header là gì?
Cache-Control Header là một trường trong phản hồi HTTP (HTTP response header) dùng để hướng dẫn trình duyệt, máy chủ proxy và CDN cách lưu tạm (cache) tài nguyên như HTML, CSS, JavaScript, hình ảnh hoặc font. Nó không phải là lệnh bắt buộc, mà là chỉ dẫn — các thành phần trung gian có thể tuân theo hoặc bỏ qua, tùy vào cấu hình và tiêu chuẩn tuân thủ.
Tại sao quan trọng trong SEO?
Tốc độ tải trang là yếu tố xếp hạng trực tiếp của Google từ năm 2010 (cho desktop) và 2018 (cho mobile). Khi tài nguyên được cache hiệu quả:
- Giảm thời gian chờ (Time to First Byte – TTFB) cho lần truy cập thứ hai trở đi;
- Hạn chế số lượng yêu cầu gửi về máy chủ gốc → giảm tải hệ thống và tăng khả năng chịu tải;
- Cải thiện Core Web Vitals: đặc biệt là First Contentful Paint (FCP) và Largest Contentful Paint (LCP) nhờ tải nhanh hơn từ bộ nhớ đệm;
- Tăng tỷ lệ giữ chân người dùng (bounce rate giảm), gián tiếp hỗ trợ thứ hạng.
Ngược lại, thiết lập sai Cache-Control (ví dụ: no-store cho file CSS tĩnh) khiến trình duyệt luôn tải lại toàn bộ tài nguyên — làm chậm trang, tiêu tốn băng thông và gây tổn hại trải nghiệm người dùng.
Cách hoạt động
Khi trình duyệt yêu cầu một tài nguyên (ví dụ: /style.css), máy chủ trả về phản hồi chứa header như:
Cache-Control: public, max-age=31536000, immutable
Trình duyệt đọc giá trị này và quyết định:
- Có lưu vào bộ nhớ đệm hay không (
public,private,no-store); - Thời gian lưu tối đa tính bằng giây (
max-age); - Có cho phép tái sử dụng mà không kiểm tra lại với máy chủ hay không (
immutable); - Có cần xác thực lại trước khi dùng lại hay không (
must-revalidate,no-cache).
Lưu ý: Các chỉ thị có thể kết hợp, nhưng nếu mâu thuẫn (ví dụ: max-age=0 và immutable), thì hành vi thực tế phụ thuộc vào trình duyệt — hiện tại hầu hết trình duyệt ưu tiên immutable khi có mặt, nhưng đây là hành vi tùy trường hợp.
Hướng dẫn thực hiện
- Xác định loại tài nguyên: Phân nhóm theo chu kỳ thay đổi — tĩnh (CSS, JS, ảnh), bán tĩnh (HTML có biến môi trường), động (trang cá nhân, kết quả tìm kiếm).
- Áp dụng giá trị phù hợp:
- Tài nguyên tĩnh (hash trong tên file):
Cache-Control: public, max-age=31536000, immutable(1 năm); - Tài nguyên bán tĩnh (HTML không hash):
Cache-Control: private, max-age=1800(30 phút); - Tài nguyên nhạy cảm (trang đăng nhập):
Cache-Control: no-storehoặcno-cache, must-revalidate.
- Tài nguyên tĩnh (hash trong tên file):
- Cấu hình trên máy chủ:
- Apache: Dùng
.htaccesshoặcVirtualHostvớiHeader set Cache-Control; - Nginx: Dùng chỉ thị
add_header Cache-Controltrong khốilocation; - Node.js/Express: Dùng middleware
res.set('Cache-Control', '...'); - CDN (Cloudflare, CloudFront): Thiết lập quy tắc cache dựa trên đường dẫn hoặc header yêu cầu.
- Apache: Dùng
- Kiểm tra kết quả: Dùng DevTools (tab Network → chọn file → xem phần Response Headers), hoặc công cụ dòng lệnh:
curl -I https://example.com/style.css.
Lỗi thường gặp
- Lỗi 1: Đặt
Cache-Control: no-cachethay vìno-storecho dữ liệu nhạy cảm→
no-cachevẫn lưu tài nguyên, nhưng bắt trình duyệt kiểm tra lại với máy chủ (quaIf-None-Match). Với trang thanh toán hoặc hồ sơ cá nhân, nên dùngno-stoređể ngăn lưu bất kỳ nơi nào. Khắc phục: Thay toàn bộ bằngno-storecho endpoint nhạy cảm. - Lỗi 2: Không dùng
immutablecho tài nguyên có hash→ Khi tên file chứa hash (ví dụ:
main.a1b2c3.js), nội dung không bao giờ thay đổi. Việc thiếuimmutablekhiến trình duyệt vẫn gửi yêu cầu xác thực (conditional request), làm chậm nhẹ. Khắc phục: Bổ sungimmutablecho tất cả tài nguyên tĩnh có hash. - Lỗi 3: Ghi đè cache ở CDN và trình duyệt cùng lúc
→ Một số CDN (như Cloudflare) có chính sách cache riêng, có thể bỏ qua header từ máy chủ nếu không bật chế độ “Respect origin cache headers”. Khắc phục: Kiểm tra cài đặt CDN, đảm bảo tùy chọn này được bật.
Ví dụ thực tế
| Tài nguyên | Đường dẫn mẫu | Cache-Control đề xuất | Ghi chú |
|---|---|---|---|
| CSS/JS có hash | /assets/main.f3a7b2.css |
public, max-age=31536000, immutable |
1 năm = 31.536.000 giây; immutable ngăn conditional request |
| Hình ảnh tĩnh | /images/logo.png |
public, max-age=604800 |
7 ngày — cân bằng giữa cập nhật và hiệu năng |
| Trang HTML gốc | /index.html |
public, max-age=1800, must-revalidate |
30 phút + bắt kiểm tra lại nếu hết hạn |
| API JSON (không nhạy cảm) | /api/latest-posts.json |
public, max-age=60 |
1 phút — đủ để giảm tải, vẫn đảm bảo độ tươi |
Câu hỏi thường gặp
Cache-Control có thay thế Expires không?
Không hoàn toàn. Expires là header cũ, dựa trên thời gian tuyệt đối (GMT), dễ sai nếu đồng hồ máy chủ lệch. Cache-Control: max-age dùng thời gian tương đối (giây kể từ lúc nhận phản hồi) nên đáng tin cậy hơn. Hiện đại nên ưu tiên Cache-Control; Expires chỉ còn dùng để hỗ trợ trình duyệt rất cũ — nhưng không bắt buộc.
max-age=0 và no-cache khác nhau thế nào?
max-age=0 nghĩa là tài nguyên hết hạn ngay khi nhận — trình duyệt vẫn lưu nhưng phải xác thực lại (via ETag hoặc Last-Modified) trước khi dùng. no-cache cũng yêu cầu xác thực, nhưng rõ ràng hơn về mặt ngữ nghĩa. Cả hai đều dẫn đến conditional request — hành vi gần như giống nhau trong thực tế.
Có nên dùng Cache-Control cho trang HTML không?
Có, nhưng phải thận trọng. Với trang tĩnh (không cá nhân hóa), có thể đặt max-age=1800 (30 phút). Với trang động (có nội dung theo người dùng), nên dùng private, max-age=0, must-revalidate hoặc no-store nếu cực kỳ nhạy cảm. Việc cache HTML giúp giảm tải máy chủ, nhưng phải đảm bảo không hiển thị nội dung lỗi thời hoặc của người khác.