KIếN THứC WEBSITE › PERFORMANCE

Cache browser headers — Cache-Control, ETag cho WordPress

Cache browser headers — Cache-Control, ETag cho WordPress 2026

HTTP cache header điều khiển trình duyệt lưu tài nguyên bao lâu và xác thực lại như thế nào. Cache-Control, ETag và Last-Modified là 3 header cốt lõi.

Cache header — tổng quan 4 lớp lưu đệm

cache browser headers — Cache header — tổng quan 4 lớp lưu đệm
Sơ đồ minh hoạ — Cache header — tổng quan 4 lớp lưu đệm

HTTP cache có 4 lớp: cache trình duyệt (bộ nhớ + ổ đĩa), cache proxy CDN, cache phía máy chủ (Redis, Memcached), và cache trang (plugin page cache).

Header Cache-Control, ETag và Last-Modified điều khiển 2 lớp đầu — trình duyệt và proxy CDN.

Luồng quyết định cache

  • Trình duyệt yêu cầu tài nguyên.
  • Kiểm tra cache trình duyệt. Còn hạn → trả về từ cache, thời gian phản hồi gần 0ms.
  • Cache hết hạn → gửi yêu cầu có điều kiện kèm If-None-Match (ETag).
  • Máy chủ trả về 304 (không thay đổi) hoặc 200 với nội dung mới.
  • Cập nhật cache → hiển thị.

Vì sao cache header quan trọng với shop VN

Shop WooCommerce trung bình có 80-150 tài nguyên tĩnh mỗi trang — CSS, JS, font, ảnh. Không có cache header đúng, trình duyệt tải lại toàn bộ mỗi lần người dùng chuyển trang.

Cache header đúng giúp tài nguyên tĩnh tải từ ổ đĩa cục bộ, giảm đáng kể số lượng yêu cầu mạng.

Cache-Control — directive chính

Cache-Control — directive chính
Cache-Control — directive chính

Cache-Control là header chuẩn HTTP/1.1, thay thế Pragma và Expires cũ. Các directive phân cách bằng dấu phẩy điều khiển hành vi cache.

Bảng directive phổ biến

Directive Mục đích Ví dụ
max-age=N Cache N giây kể từ thời điểm phản hồi max-age=31536000 (1 năm)
s-maxage=N Ghi đè max-age cho proxy/CDN s-maxage=86400 (1 ngày CDN)
public Cache chia sẻ (CDN, proxy) Tài nguyên không gắn với người dùng cụ thể
private Chỉ cache tại trình duyệt, không CDN Dữ liệu riêng từng người dùng
no-cache Cache nhưng phải xác thực lại trước khi phục vụ HTML cập nhật thường xuyên
no-store Không cache bất kỳ đâu Dữ liệu nhạy cảm (phiên đăng nhập)
immutable Tài nguyên không bao giờ thay đổi (đã có version) style.abc123.css
must-revalidate Xác thực lại nghiêm ngặt, không phục vụ bản hết hạn Tài nguyên cần luôn mới

Tổ hợp directive phổ biến

  • CSS/JS có hash trong tên file: public, max-age=31536000, immutable
  • Ảnh upload (tên file cố định): public, max-age=2592000 (1 tháng)
  • Trang HTML: public, max-age=0, must-revalidate (luôn xác thực lại)
  • API JSON động: private, no-cache
  • Phiên đăng nhập/bảo mật: no-store

Directive immutable — tiết kiệm băng thông đáng kể

Khi tên file CSS/JS chứa hash (ví dụ style.a3f91b.css), nội dung không bao giờ thay đổi sau khi build. Directive immutable báo trình duyệt không gửi yêu cầu xác thực lại trong suốt thời gian max-age.

Điều này tiết kiệm hoàn toàn lượt yêu cầu mạng cho tài nguyên đã cache — ngay cả khi người dùng nhấn F5.

ETag và Last-Modified — validator xác thực lại

ETag và Last-Modified — validator xác thực lại
ETag và Last-Modified — validator xác thực lại

Khi cache hết hạn, trình duyệt gửi yêu cầu có điều kiện kèm validator. Máy chủ kiểm tra và trả về 304 nếu không thay đổi (tiết kiệm băng thông) hoặc 200 với nội dung mới.

ETag — validator mạnh

  • Máy chủ tính hash nội dung (MD5, SHA) hoặc kết hợp inode + kích thước + thời gian sửa đổi.
  • Phản hồi: ETag: "abc123def456".
  • Yêu cầu tiếp theo từ trình duyệt: If-None-Match: "abc123def456".
  • Máy chủ so sánh ETag → trả 304 hoặc 200.
  • Strong ETag: khớp byte-by-byte. Weak ETag (W/…): tương đương về ngữ nghĩa.

Last-Modified — validator yếu

  • Máy chủ phản hồi: Last-Modified: Wed, 21 Oct 2025 07:28:00 GMT.
  • Yêu cầu tiếp theo: If-Modified-Since: Wed, 21 Oct 2025 07:28:00 GMT.
  • Máy chủ kiểm tra thời gian sửa đổi file → trả 304 hoặc 200.
  • Ít chính xác hơn ETag (độ phân giải 1 giây), có thể sai nếu khôi phục file từ bản sao lưu giữ nguyên thời gian.

Khi nào nên tắt ETag

Trên hệ thống nhiều máy chủ (load balancer), mỗi máy tính ETag khác nhau từ inode riêng. Trình duyệt nhận ETag từ máy A, gửi xác thực đến máy B → không khớp → trả 200 thay vì 304.

Trong trường hợp này, tắt ETag và dùng immutable kết hợp hash tên file sẽ hiệu quả hơn.

Khuyến nghị cache header theo loại tài nguyên

Loại tài nguyên Cache-Control Validator Lý do
CSS/JS có hash tên file public, max-age=31536000, immutable Không cần Tên file đổi khi nội dung đổi
Ảnh upload public, max-age=2592000 ETag Cache 1 tháng, xác thực lại sau
Font woff2 public, max-age=31536000, immutable Không cần Font ít thay đổi
Trang HTML public, max-age=0, must-revalidate ETag Cập nhật thường, luôn xác thực lại
API JSON private, no-cache ETag Dữ liệu riêng từng người dùng
wp-admin/ private, no-store Không cần Phiên quản trị nhạy cảm

Checklist cache header cho shop WordPress

  1. Xác nhận CSS/JS build có hash trong tên file (WP Rocket, Webpack, Vite tự làm).
  2. Set max-age=31536000, immutable cho CSS/JS có hash.
  3. Set max-age=2592000 cho ảnh trong wp-content/uploads/.
  4. Set max-age=0, must-revalidate cho HTML.
  5. Set no-store cho wp-admin/ và /checkout/.
  6. Kiểm tra header qua DevTools → Network tab → chọn resource → xem Response Headers.
  7. Xác minh không có no-cache sai vị trí trên CSS/JS tĩnh.
  8. Nếu dùng load balancer: cân nhắc tắt ETag, dùng immutable thay.

Thiết lập cache header trong WordPress

WordPress core không tự set Cache-Control chặt chẽ cho tài nguyên tĩnh. Cần cấu hình ở web server hoặc plugin.

Cấu hình qua Apache .htaccess

<IfModule mod_expires.c>
  ExpiresActive On
  # CSS/JS — 1 năm
  ExpiresByType text/css "access plus 1 year"
  ExpiresByType application/javascript "access plus 1 year"
  # Ảnh — 1 tháng
  ExpiresByType image/webp "access plus 1 month"
  ExpiresByType image/jpeg "access plus 1 month"
  ExpiresByType image/png "access plus 1 month"
  # Font — 1 năm
  ExpiresByType font/woff2 "access plus 1 year"
</IfModule>

<IfModule mod_headers.c>
  # CSS/JS có hash — thêm immutable
  <FilesMatch ".(css|js)$">
    Header append Cache-Control "public, immutable"
  </FilesMatch>
</IfModule>

Cấu hình qua Nginx

server {
  # CSS/JS có hash — cache 1 năm
  location ~* .(css|js)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
  }

  # Ảnh — cache 1 tháng
  location ~* .(jpg|jpeg|png|webp|svg)$ {
    expires 30d;
    add_header Cache-Control "public";
  }

  # Font — cache 1 năm
  location ~* .(woff2|woff)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
  }
}

Cấu hình qua plugin

WP Rocket và LiteSpeed Cache có giao diện cấu hình cache header trực quan. WP Rocket tự nhận diện và áp đặt header phù hợp cho CSS/JS minified.

Kết hợp cache header đúng với Cloudflare CDN giúp tài nguyên tĩnh gần như không bao giờ phải tải lại từ máy chủ gốc. Xem thêm hướng dẫn cấu hình Cloudflare cho WordPress.

Kiểm tra header qua DevTools

Mở DevTools (F12) → tab “Network” → chọn resource → xem mục “Response Headers”. Tìm dòng cache-control để xác nhận giá trị đúng.

Tải lại trang lần hai và kiểm tra cột “Size”. Nếu hiển thị “(disk cache)” hoặc “(memory cache)” thay vì số byte, cache đang hoạt động đúng.

Cache header và các chỉ số hiệu năng

Cache header ảnh hưởng trực tiếp đến LCP và TTFB — hai chỉ số quan trọng nhất trong Core Web Vitals 2026. Tài nguyên tĩnh cache đúng giúp LCP giảm rõ rệt trên lượt xem trang thứ hai.

Đọc thêm về tối ưu TTFB tại TTFB — phân tích chuyên sâu và cách Redis hỗ trợ cache phía server tại Redis cache cho WordPress.

Bài liên quan

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

WordPress mặc định dùng Cache-Control gì?

WordPress core không set Cache-Control chặt cho tài nguyên tĩnh. Apache/.htaccess hoặc Nginx và plugin (WP Rocket, LiteSpeed Cache) phải ghi đè.

Mặc định thường chỉ là max-age=600 (10 phút) — không đủ tốt.

Plugin WP Rocket khi kích hoạt sẽ tự động thêm rule .htaccess để set max-age 1 năm cho CSS/JS và ảnh.

ETag có nên tắt không?

Tắt ETag hợp lý khi dùng nhiều máy chủ phía sau load balancer. Mỗi máy tính ETag khác nhau (từ inode riêng), gây ra cache miss không cần thiết.

Thay vào đó: dùng immutable kết hợp hash tên file cho CSS/JS, và Last-Modified cho ảnh. Hiệu quả hơn ETag trong môi trường đa máy chủ.

no-cache khác no-store ở điểm nào?

no-cache nghĩa là cache được, nhưng phải xác thực lại với máy chủ trước mỗi lần phục vụ. no-store là tuyệt đối không lưu cache bất kỳ đâu — kể cả trình duyệt, proxy, CDN.

Dùng no-store cho dữ liệu nhạy cảm (phiên đăng nhập, thông tin thanh toán). Dùng no-cache cho HTML cập nhật thường xuyên nhưng không nhạy cảm.

Cache-Control được set ở đâu trong WordPress?

Có 3 nơi để set:

  • Web server (.htaccess Apache hoặc nginx.conf) — áp dụng mọi request không qua PHP, hiệu quả nhất.
  • Plugin (WP Rocket, LiteSpeed Cache) — có giao diện cấu hình trực quan.
  • PHP headerheader('Cache-Control: ...') trong code tùy biến.

Cấu hình ở web server luôn được ưu tiên vì không qua PHP và áp dụng sớm nhất trong chuỗi xử lý.

Sau khi cập nhật plugin, cache CSS/JS cũ bị xóa chưa?

Nếu CSS/JS có hash tên file (WP Rocket, Webpack tự làm) — tên file mới khác tên cũ, trình duyệt tự tải mới. Nếu không có hash, trình duyệt vẫn phục vụ bản cũ cho đến khi hết TTL.

Giải pháp: bật “Query String Versioning” trong WP Rocket, hoặc cấu hình build tool tạo hash file tự động.

Cần kiểm tra cache header toàn bộ shop đang hoạt động như thế nào? Web22 tư vấn audit tối ưu Core Web Vitals bao gồm kiểm tra cache header và CDN end-to-end.