Technical SEO

HTTP Caching Headers

Các header như Cache-Control, Expires, Vary giúp kiểm soát hành vi lưu cache của trình duyệt và CDN.

4 lượt xem Cập nhật: 26/05/2026

HTTP Caching Headers là gì?

HTTP Caching Headers là các dòng thông tin trong phản hồi HTTP (response headers) giúp trình duyệt, máy chủ proxy và CDN hiểu cách lưu trữ, tái sử dụng hoặc bỏ qua nội dung đã tải trước đó. Chúng không phải là tính năng riêng của website hay công cụ SEO — mà là phần thiết yếu của giao thức HTTP/1.1 và HTTP/2, được quy định rõ trong RFC 7234.

Tại sao quan trọng trong SEO?

Các header này ảnh hưởng trực tiếp đến tốc độ tải trang, hiệu suất trải nghiệm người dùng và mức tiêu thụ băng thông — ba yếu tố nằm trong tín hiệu xếp hạng của Google (Core Web Vitals, thời gian tải, TTFB). Khi nội dung được cache đúng cách:

  • Trình duyệt không gửi yêu cầu mới tới máy chủ cho tài nguyên tĩnh (CSS, JS, hình ảnh), giảm số lần gọi server;
  • CDN phục vụ nội dung từ edge location gần người dùng nhất, cắt giảm latency;
  • Tỷ lệ bounce rate giảm do trang tải nhanh hơn — đặc biệt trên thiết bị di động và mạng chậm;
  • Máy chủ web giảm tải, ổn định hơn khi có lượt truy cập cao đột biến.

Ngược lại, cấu hình sai có thể khiến Googlebot tải phiên bản cũ (hoặc lỗi) của trang, làm chậm quá trình lập chỉ mục hoặc gây hiện tượng hiển thị nội dung không đồng bộ giữa người dùng và bot.

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ề nội dung kèm các header như Cache-Control hoặc Expires. Trình duyệt đọc các giá trị này để quyết định:

  1. Có lưu bản sao vào bộ nhớ cache hay không;
  2. Lưu trong bao lâu;
  3. Khi nào cần kiểm tra lại tính mới (revalidation) với máy chủ;
  4. Có nên dùng cache chung (shared cache như CDN) hay chỉ cache riêng (private cache như trình duyệt cá nhân).

Quy trình này tuân thủ thứ tự ưu tiên: Cache-Control luôn được ưu tiên hơn Expires; nếu cả hai đều có, Expires bị bỏ qua.

Hướng dẫn thực hiện

Để cấu hình đúng, bạn cần xác định loại tài nguyên và chu kỳ cập nhật của chúng:

  1. Tài nguyên tĩnh không thay đổi (logo.png, font.woff2, main.css): dùng Cache-Control: public, max-age=31536000, immutable (1 năm); thêm immutable để ngăn trình duyệt gửi yêu cầu revalidation.
  2. Tài nguyên thay đổi theo phiên bản (app.js?v=2.4.1): dùng Cache-Control: public, max-age=31536000, kết hợp kỹ thuật cache-busting bằng query string hoặc hash trong tên file.
  3. Tài nguyên động nhưng ít thay đổi (trang danh mục sản phẩm): dùng Cache-Control: public, max-age=3600, stale-while-revalidate=86400 (cache 1 giờ, vẫn phục vụ trong 24 giờ nếu máy chủ chậm phản hồi).
  4. Tài nguyên cá nhân, nhạy cảm (trang hồ sơ người dùng): dùng Cache-Control: private, no-store — cấm lưu ở bất kỳ đâu ngoài trình duyệt người dùng.

Với Apache: thêm vào .htaccess hoặc virtual host:

Header set Cache-Control "public, max-age=31536000, immutable" "expr=%{REQUEST_FILENAME} =~ m#\.(css|js|png|jpg|webp|woff2)$#"

Với Nginx: khai báo trong khối location:

location ~* \.(css|js|png|jpg|webp|woff2)$ { add_header Cache-Control "public, max-age=31536000, immutable"; }

Lưu ý: Luôn kiểm tra kết quả bằng công cụ như WebPageTest hoặc tab Network trong DevTools (cột Size → “from disk cache” hoặc “from memory cache”).

Lỗi thường gặp

  • Lỗi 1: Đặt max-age=0 hoặc no-cache cho toàn bộ tài nguyên — khiến trình duyệt luôn gọi lại máy chủ, tăng TTFB và tải server. Cách khắc phục: Phân loại tài nguyên, chỉ áp no-cache cho nội dung động thực sự cần kiểm tra mỗi lần.
  • Lỗi 2: Dùng Expires mà không kèm Cache-Control — dễ gây xung đột vì Expires dựa trên múi giờ máy chủ, dễ sai lệch. Cách khắc phục: Ưu tiên Cache-Control; nếu bắt buộc dùng Expires, đảm bảo giá trị sau thời điểm hiện tại và khớp với max-age.
  • Lỗi 3: Bỏ qua header Vary khi dùng nén (gzip/brotli) hoặc hỗ trợ nhiều định dạng ảnh — dẫn đến việc CDN trả nhầm phiên bản (ví dụ: trả file nén cho trình duyệt không hỗ trợ). Cách khắc phục: Thêm Vary: Accept-Encoding, Accept khi phục vụ nhiều định dạng (WebP + JPEG) hoặc nén.

Ví dụ thực tế

Dưới đây là bảng so sánh cấu hình cache cho các loại tài nguyên phổ biến trên website thương mại điện tử:

Tài nguyên Cache-Control đề xuất Ghi chú
Hình sản phẩm (.webp) public, max-age=2592000, immutable 1 tháng — đủ dài cho ảnh không đổi, immutable tránh revalidation
Tệp JavaScript chính (.js) public, max-age=31536000, immutable 1 năm — chỉ thay đổi khi đổi tên file (hash-based)
Trang danh mục (/danh-muc/) public, max-age=1800, stale-while-revalidate=3600 30 phút cache, vẫn phục vụ tối đa 1 giờ nếu backend chậm
API dữ liệu người dùng (/api/profile) private, no-store Không lưu cache ở bất kỳ đâu ngoài trình duyệt người dùng

Câu hỏi thường gặp

Cache-Control và Expires khác nhau thế nào?

Cache-Control là header hiện đại, hỗ trợ nhiều chỉ thị (max-age, immutable, stale-while-revalidate…) và ưu tiên cao hơn. Expires chỉ nhận giá trị thời gian tuyệt đối (GMT), dễ lỗi do chênh lệch múi giờ hoặc đồng hồ máy chủ. Từ năm 2015, tất cả trình duyệt hiện đại đều hỗ trợ Cache-Control đầy đủ — nên dùng nó thay vì Expires trừ trường hợp tương thích ngược bắt buộc.

Vary header có cần thiết với mọi website?

Chỉ cần khi bạn phục vụ nhiều phiên bản cùng một URL — ví dụ: nén (gzip vs brotli), định dạng ảnh (WebP vs JPEG), hoặc ngôn ngữ (vi vs en). Nếu không dùng các kỹ thuật này, Vary không cần thiết. Việc thêm dư thừa có thể làm giảm tỷ lệ hit cache trên CDN.

Có nên dùng stale-while-revalidate cho mọi trang tĩnh?

Không. Chỉ nên dùng khi nội dung có thể chấp nhận hiển thị phiên bản cũ trong thời gian ngắn (ví dụ: blog, danh mục), và bạn muốn duy trì tốc độ khi backend tạm chậm. Với trang thanh toán hoặc thông tin thời gian thực (tỷ giá, chứng khoán), nên tránh — vì tính chính xác quan trọng hơn tốc độ. Việc triển khai cũng phụ thuộc vào khả năng hỗ trợ của CDN và trình duyệt (Chrome, Firefox, Edge hỗ trợ tốt; Safari hỗ trợ từ phiên bản 16.4 trở lên — tùy trường hợp).