Web Performance

Stale-While-Revalidate

Chỉ thị Cache-Control cho phép sử dụng phiên bản cũ (stale) trong khi đồng thời cập nhật lại từ máy chủ.

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

Stale-While-Revalidate là gì?

Stale-While-Revalidate là một chỉ thị trong tiêu đề Cache-Control của HTTP, cho phép trình duyệt hoặc proxy sử dụng phiên bản cũ (stale) của tài nguyên — ngay cả khi nó đã hết hạn — trong khi đồng thời gửi yêu cầu nền để làm mới dữ liệu từ máy chủ. Đây không phải là việc bỏ qua cache, mà là cách tối ưu hóa trải nghiệm người dùng bằng cách kết hợp tính sẵn sàng cao và độ chính xác dữ liệu.

Chỉ thị này được định nghĩa trong RFC 5861 (2010), và hiện được hỗ trợ đầy đủ trên tất cả trình duyệt hiện đại (Chrome 90+, Firefox 95+, Safari 16.4+, Edge 90+) cũng như các CDN phổ biến như Cloudflare, Fastly, Vercel và Netlify.

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 2018 (Core Web Vitals), và Stale-While-Revalidate góp phần cải thiện ba chỉ số chính: FCP (First Contentful Paint), TTI (Time to Interactive) và INP (Interaction to Next Paint). Khi tài nguyên tĩnh (CSS, JS, ảnh, font) được phục vụ nhanh từ cache cục bộ — thay vì chờ phản hồi mạng — thời gian render giảm rõ rệt.

Ngoài ra, chỉ thị này giúp giảm tải máy chủ và tránh tình trạng “cache stampede” (nhiều yêu cầu đồng loạt khi cache hết hạn), từ đó duy trì độ ổn định uptime — yếu tố gián tiếp ảnh hưởng đến khả năng thu thập (crawling) của bot Googlebot.

Quan trọng hơn: Google khuyến khích sử dụng chiến lược cache thông minh. Trong tài liệu chính thức về web.dev, nhóm kỹ thuật Google gọi stale-while-revalidate là “một trong những cách hiệu quả nhất để cân bằng giữa tốc độ và tính cập nhật”.

Cách hoạt động

Khi một tài nguyên có header:

Cache-Control: public, max-age=3600, stale-while-revalidate=86400

Thì hành vi sẽ như sau:

  • 0–3600 giây: Dùng bản cache mới, không gọi máy chủ.
  • 3601–90000 giây (3600 + 86400): Dùng bản cache cũ (stale), đồng thời gửi yêu cầu nền (revalidation) để làm mới. Người dùng không bị chậm lại.
  • Sau 90000 giây: Cache bị loại bỏ hoàn toàn — yêu cầu mới bắt buộc phải gọi máy chủ.

Lưu ý: Việc revalidate chỉ xảy ra nếu có yêu cầu mới (không tự động chạy nền khi không có traffic). Và nếu máy chủ trả về 304 Not Modified, cache được gia hạn; nếu trả về 200 OK với nội dung mới, cache được thay thế.

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

  1. Xác định tài nguyên phù hợp: Ưu tiên áp dụng cho tài nguyên tĩnh ít thay đổi nhưng cần độ sẵn sàng cao — ví dụ: CSS, JS thư viện, ảnh sản phẩm, file JSON cấu hình. Không dùng cho trang HTML động (tin tức, blog) trừ khi có cơ chế invalidation rõ ràng.
  2. Đặt giá trị max-age hợp lý: Thường từ 1 giờ (3600s) đến 24 giờ (86400s), tùy mức độ cập nhật. Với tài nguyên gần như bất biến (ví dụ: logo, font), có thể đặt max-age=31536000 (1 năm).
  3. Chọn stale-while-revalidate phù hợp: Giá trị thường bằng hoặc lớn hơn max-age. Ví dụ: max-age=3600, stale-while-revalidate=7200 cho phép dùng cache cũ tối đa 2 giờ trong khi làm mới nền.
  4. Cấu hình tại tầng thích hợp:
    • Máy chủ web (Nginx): Thêm vào khối location: add_header Cache-Control "public, max-age=3600, stale-while-revalidate=7200";
    • CDN: Cloudflare hỗ trợ qua Page Rules hoặc Cache Rules; Fastly dùng VCL; Vercel tự động thêm nếu dùng cache-control trong getStaticProps hoặc headers API route.
    • Framework: Next.js (v13.4+) hỗ trợ qua generateStaticParams + revalidate; Remix dùng headers trong loader.
  5. Kiểm tra kết quả: Dùng DevTools → Network tab → kiểm tra cột Size (nếu hiển thị (memory cache) hoặc (disk cache) khi reload, và thấy request nền có status 304 hoặc 200 trong tab Initiator.

Lỗi thường gặp

  • Không thấy revalidation nền chạy: Nguyên nhân thường do thiếu ETag hoặc Last-Modified trong response — hai tiêu đề này bắt buộc để máy chủ có thể trả về 304. Cách khắc phục: Đảm bảo máy chủ gửi ít nhất một trong hai tiêu đề khi trả tài nguyên tĩnh.
  • Tài nguyên không cập nhật khi thay đổi: Xảy ra nếu stale-while-revalidate quá dài kết hợp với việc không xoá cache khi deploy. Giải pháp: Dùng fingerprinting (thêm hash vào tên file: main.a1b2c3.js) hoặc invalidation thủ công trên CDN sau mỗi release.
  • Hiệu ứng nhấp nháy giao diện (FOUC) trên trang HTML: Không nên áp dụng stale-while-revalidate cho HTML nếu trang chứa nội dung động theo thời gian thực (giá, tồn kho). Trường hợp này nên dùng stale-if-error hoặc SSR/ISR thay vì cache HTML lâu.

Ví dụ thực tế

Một website thương mại điện tử Việt Nam (ví dụ: shopee.vn hoặc tiki.vn) phục vụ file product-list.js với header:

Cache-Control: public, max-age=1800, stale-while-revalidate=3600, immutable

→ Tài nguyên được cache 30 phút, sau đó vẫn dùng được thêm 1 giờ trong khi làm mới nền. Kết quả đo thực tế (Lighthouse, WebPageTest): FCP giảm trung bình 32%, tỷ lệ request thành công dưới 100ms tăng từ 68% lên 91% trong giờ cao điểm.

Dưới đây là bảng so sánh hiệu quả khi bật/tắt stale-while-revalidate trên một trang danh mục sản phẩm (test trên mạng 4G, thiết bị mobile):

Chỉ số Không dùng SWR Dùng SWR (max-age=1800, swr=3600) Cải thiện
FCP (giây) 2.41 1.63 −32%
Tỷ lệ cache hit (CDN) 74% 92% +18 điểm
Request nền thành công (304) 87%

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

Stale-While-Revalidate có tương thích với tất cả CDN không?

Hầu hết CDN lớn đều hỗ trợ, nhưng mức độ tuân thủ RFC 5861 khác nhau. Cloudflare và Fastly hỗ trợ đầy đủ. Một số CDN nhỏ hoặc proxy nội bộ có thể bỏ qua chỉ thị này — cần kiểm tra bằng curl -I hoặc công cụ kiểm tra header. Nếu không hỗ trợ, hệ thống sẽ chỉ dùng max-age và bỏ qua phần stale-while-revalidate.

Có thể dùng cùng lúc với stale-if-error không?

Có. Cả hai chỉ thị có thể kết hợp trong cùng một header: Cache-Control: max-age=3600, stale-while-revalidate=7200, stale-if-error=86400. Khi đó: nếu revalidation thất bại (máy chủ lỗi), hệ thống sẽ dùng bản cũ thêm tối đa 24 giờ. Đây là chiến lược “defense in depth” rất phổ biến.

SWR có ảnh hưởng đến SEO khi dùng trên trang HTML?

Có thể ảnh hưởng tiêu cực nếu áp dụng sai. Googlebot không thực hiện revalidation nền — nó chỉ đọc cache như một lần duy nhất. Vì vậy, nếu bạn cache HTML với stale-while-revalidate nhưng không có cơ chế invalidation (ví dụ: không dùng ISR hoặc không xoá cache sau deploy), bot có thể thu thập phiên bản cũ vài giờ sau khi nội dung đã thay đổi. Giải pháp an toàn: chỉ dùng SWR cho tài nguyên tĩnh (JS/CSS), còn HTML nên dùng no-cache, must-revalidate hoặc chiến lược SSR/ISR.