Hướng Dẫn HTTP Caching: Tăng Tốc Website Mà Không Cần Thêm Hạ Tầng
Nắm vững Cache-Control headers, ETags, chiến lược CDN caching và hành vi cache của trình duyệt để cải thiện đáng kể tốc độ tải trang và giảm chi phí máy chủ.
Caching là tối ưu hiệu suất có đòn bẩy cao nhất mà bạn có thể thực hiện. Một phản hồi được cache được phục vụ trong micro giây, không tốn chi phí và không cần bất kỳ xử lý nào từ máy chủ. Thế nhưng hầu hết các ứng dụng đều cache quá mức (phục vụ nội dung cũ) hoặc không cache gì cả (lãng phí băng thông và tài nguyên tính toán). Hiểu rõ HTTP caching sẽ biến nó từ nguồn gây lỗi thành một vũ khí mạnh mẽ.
HTTP caching hoạt động như thế nào
Khi trình duyệt hoặc CDN nhận được một phản hồi, nó kiểm tra các header để quyết định có cache hay không và cache trong bao lâu. Ở lần yêu cầu tiếp theo cho cùng tài nguyên đó, hệ thống có thể phục vụ bản sao đã cache — mà không cần động đến máy chủ của bạn.
Vòng đời cache có hai giai đoạn:
- Freshness (Độ tươi) — Bản sao đã cache còn hợp lệ không? Được xác định bởi
Cache-Control: max-agehoặcExpires. - Validation (Xác thực) — Nếu đã cũ, ta có thể xác nhận với máy chủ rằng nội dung chưa thay đổi không? Được xác định bởi
ETaghoặcLast-Modified.
Cache-Control: chỉ thị caching chính
Cache-Control là header caching mạnh nhất. Đây là danh sách các chỉ thị phân cách bằng dấu phẩy:
Cache-Control: public, max-age=31536000, immutable
Các chỉ thị quan trọng
| Chỉ thị | Ý nghĩa |
|---|---|
public |
Bất kỳ cache nào (trình duyệt, CDN, proxy) đều có thể lưu trữ |
private |
Chỉ trình duyệt của người dùng cuối được cache (không áp dụng cho CDN) |
no-cache |
Phải xác thực lại với máy chủ trước mỗi lần dùng (không có nghĩa là "không cache") |
no-store |
Không bao giờ cache — dành cho dữ liệu nhạy cảm |
max-age=N |
Cache trong N giây |
s-maxage=N |
Thời gian tối đa dành riêng cho CDN (ghi đè max-age đối với shared cache) |
immutable |
Nội dung sẽ không bao giờ thay đổi — bỏ qua việc xác thực lại hoàn toàn |
must-revalidate |
Khi đã cũ, phải xác thực lại trước khi phục vụ |
stale-while-revalidate=N |
Phục vụ bản cũ trong N giây trong khi tải bản mới ở nền |
⚠️
no-cacheKHÔNG có nghĩa là "không cache." Nó có nghĩa là "cache lại, nhưng luôn kiểm tra xem có còn hợp lệ không." Hãy dùngno-storenếu bạn thực sự không muốn cache bất kỳ thứ gì.
Chiến lược caching theo loại tài nguyên
Các tài nguyên khác nhau cần chiến lược khác nhau:
Tài nguyên tĩnh có content hashing (CSS, JS, images)
Cache-Control: public, max-age=31536000, immutable
Nếu công cụ build của bạn thêm hash vào tên file (main.a3f9b2c.js), URL sẽ thay đổi khi nội dung thay đổi. Cache mãi mãi — mọi phiên bản mới sẽ có URL mới.
Trang HTML
Cache-Control: no-cache
Hoặc với TTL ngắn:
Cache-Control: public, max-age=60, stale-while-revalidate=3600
HTML thay đổi thường xuyên và liên kết đến các tài nguyên đã hash. Cache trong thời gian ngắn hoặc bắt buộc xác thực lại.
Phản hồi API
# Dữ liệu công khai (ví dụ: danh mục sản phẩm)
Cache-Control: public, max-age=300, stale-while-revalidate=600
# Dữ liệu theo từng người dùng
Cache-Control: private, max-age=60
# Dữ liệu thời gian thực (giá cổ phiếu, điểm trực tiếp)
Cache-Control: no-store
Dữ liệu nhạy cảm (xác thực, thanh toán)
Cache-Control: no-store
Không bao giờ cache. Dứt khoát.
ETags và conditional requests
Khi một phản hồi đã cache hết hạn, trình duyệt không chỉ đơn giản bỏ nó đi — mà hỏi máy chủ xem nó có còn hợp lệ không. Đây là quá trình revalidation (xác thực lại).
ETags
ETag là dấu vân tay của nội dung phản hồi:
# Máy chủ gửi:
ETag: "a3f9b2c8d4e1"
# Yêu cầu tiếp theo của trình duyệt:
If-None-Match: "a3f9b2c8d4e1"
# Nếu không thay đổi, máy chủ phản hồi:
HTTP/1.1 304 Not Modified
(không có body — tiết kiệm băng thông)
# Nếu đã thay đổi, máy chủ phản hồi:
HTTP/1.1 200 OK
ETag: "b7c2d4e9a1f3"
(phản hồi mới đầy đủ)
Phản hồi 304 Not Modified không có body — chỉ có headers. Điều này tiết kiệm toàn bộ băng thông cần thiết để tải lại nội dung.
Last-Modified
Tương tự nhưng dùng timestamp thay vì hash:
Last-Modified: Tue, 01 Apr 2026 10:00:00 GMT
# Trình duyệt gửi:
If-Modified-Since: Tue, 01 Apr 2026 10:00:00 GMT
ETags đáng tin cậy hơn (timestamp có thể không chính xác với các máy chủ load-balanced).
Vary header: cache theo từng biến thể yêu cầu
Header Vary cho các cache biết header nào của yêu cầu ảnh hưởng đến phản hồi:
Vary: Accept-Encoding
Điều này cache một bản sao riêng cho phản hồi gzip và br. Các trường hợp sử dụng phổ biến:
Vary: Accept-Encoding # Cache riêng cho nén/không nén
Vary: Accept-Language # Cache riêng theo từng ngôn ngữ
Vary: Accept # Cache riêng cho phản hồi JSON và HTML
⚠️
Vary: CookiehoặcVary: Authorizationthực chất vô hiệu hóa CDN caching — CDN không thể cache các phản hồi theo từng người dùng.
stale-while-revalidate: làm mới ở nền
Đây là một trong những mẫu caching hiện đại hữu ích nhất:
Cache-Control: max-age=60, stale-while-revalidate=600
- Phục vụ ngay lập tức từ cache trong 60 giây đầu tiên (còn tươi)
- Với các yêu cầu từ giây 60–660: phục vụ bản cũ ngay lập tức, nhưng tải phiên bản mới ở nền
- Sau 660 giây: phải xác thực lại trước khi phục vụ
Người dùng luôn nhận được phản hồi nhanh. Cache luôn được cập nhật mà không bắt ai phải chờ một vòng kết nối mạng.
Những lưu ý khi dùng CDN caching
CDN (Cloudflare, CloudFront, Fastly) tuân theo các header Cache-Control nhưng thêm một lớp phức tạp riêng:
-
s-maxagecho phép bạn đặt TTL khác nhau cho CDN và trình duyệt:Cache-Control: public, max-age=60, s-maxage=86400Trình duyệt cache 1 phút; CDN cache 24 giờ.
-
Cache purging — khi deploy, hãy xóa CDN cache cho các tài nguyên đã cập nhật. Hầu hết CDN đều cung cấp purging qua API.
-
Cache keys — CDN cache dựa trên URL + Vary headers. Query strings thường được đưa vào cache key.
Kiểm tra hành vi cache
Dùng API Request Builder của chúng tôi để kiểm tra response headers và xác minh cấu hình caching của bạn đang hoạt động đúng:
- Thực hiện một yêu cầu và kiểm tra các header
Cache-Control,ETagvàLast-Modified - Thực hiện lại cùng yêu cầu đó — kiểm tra header
Age(số giây kể từ khi cache) vàX-Cache: HIT - Kiểm tra
CF-Cache-Status(Cloudflare) hoặcX-Cache(CloudFront) để xác nhận CDN caching
Trong Chrome DevTools → tab Network → nhấp vào một tài nguyên → tab Headers — tìm from disk cache hoặc from memory cache trong phần response.
Cấu hình Nginx caching
Cấu hình caching headers ở cấp độ Nginx để đảm bảo hành vi nhất quán:
# Tài nguyên tĩnh — cache mãi mãi
location ~* \.(js|css|woff2|png|jpg|webp|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# HTML — luôn xác thực lại
location ~* \.html$ {
add_header Cache-Control "no-cache";
}
# API — cache ngắn với stale-while-revalidate
location /api/ {
add_header Cache-Control "public, max-age=60, stale-while-revalidate=600";
}
Dùng Nginx Config Generator của chúng tôi để tạo cấu hình Nginx hoàn chỉnh và tối ưu cho trường hợp của bạn.
Danh sách kiểm tra caching
- Tài nguyên tĩnh (CSS/JS) dùng tên file có content hash +
max-age=31536000, immutable - HTML được phục vụ với
no-cachehoặcmax-agerất ngắn - Phản hồi API được cache dựa trên tần suất cập nhật
- Dữ liệu nhạy cảm dùng
no-store - ETags hoặc
Last-Modifiedđược bật cho conditional requests -
stale-while-revalidateáp dụng cho các API endpoint phù hợp - CDN caching headers đã được xác minh và kiểm tra
HTTP caching đúng cách giúp website của bạn cảm giác tức thì với những khách quay lại, giảm chi phí băng thông và giảm tải máy chủ — tất cả mà không cần thêm bất kỳ hạ tầng nào.