Web Performance

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).

3 lượt xem Cập nhật: 30/05/2026

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)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=0immutable), 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

  1. 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).
  2. Á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-store hoặc no-cache, must-revalidate.
  3. Cấu hình trên máy chủ:
    • Apache: Dùng .htaccess hoặc VirtualHost với Header set Cache-Control;
    • Nginx: Dùng chỉ thị add_header Cache-Control trong khối location;
    • 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.
  4. 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-cache thay vì no-store cho dữ liệu nhạy cảm

    no-cache vẫ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ủ (qua If-None-Match). Với trang thanh toán hoặc hồ sơ cá nhân, nên dùng no-store để ngăn lưu bất kỳ nơi nào. Khắc phục: Thay toàn bộ bằng no-store cho endpoint nhạy cảm.

  • Lỗi 2: Không dùng immutable cho 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ếu immutable khiế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ổ sung immutable cho 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.