Cache-Control — định nghĩa, chỉ thị và quản lý bộ nhớ đệm

Tác giả: IT Sectr Đã đăng: 2026-03-09 Thời gian đọc: 9 phút

Cache-Control là một tiêu đề HTTP xác định các quy tắc lưu trữ đệm tài nguyên trên phía máy khách, máy chủ proxy và CDN bằng một tập hợp các chỉ thị. Không giống như tiêu đề Expires cũ, Cache-Control hỗ trợ hàng chục kết hợp: max-age đặt thời gian sống tính bằng giây, private và public kiểm soát khả năng sử dụng bộ nhớ đệm, no-cache và no-store — xác minh bắt buộc. Theo Google Web Dev (2025), cấu hình Cache-Control đúng cách có thể giảm thời gian tải trang 50-80% cho các lần truy cập lặp lại. Điều này làm cho tiêu đề này trở nên quan trọng đối với hiệu suất của ứng dụng web và di động.

Ý chính

  • Cache-Control — một tiêu đề HTTP với các chỉ thị kiểm soát bộ nhớ đệm trên máy khách, proxy và CDN
  • max-age — một chỉ thị chính đặt thời gian sống của tài nguyên tính bằng giây mà không cần xác minh lại
  • private vs public — private chỉ cho phép bộ nhớ đệm trên máy khách, public cũng trên proxy và CDN
  • no-cache vs no-store — no-cache yêu cầu xác minh trước khi sử dụng, no-store cấm hoàn toàn bộ nhớ đệm
  • s-maxage — ghi đè max-age cho bộ nhớ đệm dùng chung mà không ảnh hưởng đến trình duyệt

Cache-Control là gì?

Cache-Control là một tiêu đề HTTP, được chuẩn hóa trong HTTP/1.1 (RFC 7234), cho phép máy chủ chỉ định cách thức và thời gian máy khách, proxy và CDN có thể lưu trữ đệm phản hồi. Không giống Expires (HTTP/1.0), Cache-Control sử dụng các chỉ thị — các lệnh văn bản kết hợp bằng dấu phẩy: Cache-Control: public, max-age=3600, must-revalidate. Tiêu đề này cung cấp khả năng kiểm soát chi tiết đối với từng mắt xích trong chuỗi bộ nhớ đệm.

Bộ nhớ đệm là một trong những cơ chế cơ bản của hiệu suất ứng dụng web và di động. Nếu không có nó, mọi yêu cầu của người dùng sẽ đi trực tiếp đến máy chủ, gây ra tải quá mức và độ trễ. Cache-Control xác định ba cấp độ bộ nhớ đệm: trình duyệt/ứng dụng (bộ nhớ đệm riêng), máy chủ proxy (bộ nhớ đệm dùng chung), và CDN (bộ nhớ đệm phân tán). Mỗi cấp độ diễn giải các chỉ thị khác nhau.

Cấu hình Cache-Control không đúng là một trong những nguyên nhân phổ biến nhất gây ra sự cố hiệu suất. Bộ nhớ đệm quá tích cực khiến người dùng thấy dữ liệu cũ. Bộ nhớ đệm quá yếu dẫn đến các yêu cầu quá mức đến máy chủ và tải chậm. Theo Akamai (2025), tối ưu hóa Cache-Control cho nội dung tĩnh giảm tải máy chủ 70-90% và cải thiện thời gian tải 40-60% cho người dùng di động.

Lịch sử của tiêu đề

Cache-Control xuất hiện trong HTTP/1.1 (RFC 2616, 1999) để thay thế Expires. Expires có một vấn đề cơ bản: nó sử dụng ngày tuyệt đối phụ thuộc vào múi giờ của máy chủ và máy khách. Cache-Control đã giải quyết vấn đề này bằng cách chuyển sang thời gian tương đối (max-age tính bằng giây kể từ thời điểm nhận được phản hồi). Sau đó, trong RFC 7234 (2014), các chỉ thị mới đã được thêm vào: immutable cho tài sản tĩnh, stale-while-revalidate và stale-if-error cho xác minh trễ.

Các chỉ thị Cache-Control

Cache-Control bao gồm hơn 10 chỉ thị được chia thành ba nhóm: chỉ thị yêu cầu (máy khách → máy chủ), chỉ thị phản hồi (máy chủ → máy khách) và phần mở rộng. Trong thực tế, phát triển di động sử dụng 6-7 chỉ thị phản hồi chính bao phủ 95% các kịch bản bộ nhớ đệm. Hãy xem xét từng chỉ thị với các ví dụ và khuyến nghị.

Chỉ thịÝ nghĩaVí dụ
max-ageThời gian sống tính bằng giây kể từ phản hồimax-age=3600 — 1 giờ
s-maxagemax-age cho bộ nhớ đệm dùng chung (proxy, CDN)s-maxage=86400 — 1 ngày cho CDN
publicCho phép mọi người lưu trữ đệm (bao gồm proxy)public, max-age=3600
privateChỉ cho phép bộ nhớ đệm trên trình duyệt/ứng dụngprivate, max-age=600
no-cacheKhông sử dụng nếu không xác minh (cần 304)no-cache
no-storeCấm hoàn toàn lưu trữ đệmno-store
must-revalidateSau max-age, phải xác minh lại với nguồn gốcmax-age=3600, must-revalidate
immutableTài nguyên sẽ không thay đổi (cho tài sản tĩnh có phiên bản)max-age=31536000, immutable

max-age là chỉ thị quan trọng nhất. Nó cấm máy khách gửi yêu cầu đến máy chủ trong thời gian đã chỉ định. Đối với tài sản tĩnh (CSS, JS, hình ảnh), max-age thường được đặt từ 1 ngày đến 1 năm. Đối với phản hồi API — từ 0 giây (dữ liệu luôn mới) đến 5-10 phút (dữ liệu tham khảo). s-maxage cho phép đặt thời gian sống khác nhau cho CDN và trình duyệt: CDN lưu trữ bản sao trong 1 ngày, trình duyệt trong 1 giờ.

no-cache và no-store

Hai chỉ thị này thường bị nhầm lẫn. no-cache không cấm bộ nhớ đệm — nó yêu cầu xác minh bản sao đã lưu trữ đệm ở mỗi lần sử dụng thông qua yêu cầu có điều kiện (If-Modified-Since hoặc If-None-Match). Nếu máy chủ phản hồi 304 — máy khách sử dụng bộ nhớ đệm. Nếu 200 — nó cập nhật. no-store, mặt khác, hoàn toàn cấm lưu phản hồi trong bất kỳ bộ nhớ đệm nào, bao gồm cả đĩa và bộ nhớ. Chỉ sử dụng no-store cho dữ liệu nhạy cảm — token, dữ liệu thanh toán, tài liệu cá nhân.

Cache-Control và Expires

Tiêu đề Expires (HTTP/1.0) cũng chỉ định thời gian sống của tài nguyên nhưng sử dụng ngày tuyệt đối: Expires: Thu, 03 Jul 2026 12:00:00 GMT. Cache-Control max-age sử dụng thời gian tương đối kể từ thời điểm phản hồi. Sự khác biệt này rất quan trọng đối với các hệ thống phân tán: nếu máy chủ và máy khách ở các múi giờ khác nhau, Expires có thể bị hiểu sai. Cache-Control không có vấn đề này — 3600 giây luôn là 3600 giây.

Khi cả hai tiêu đề đều có mặt, Cache-Control được ưu tiên hơn Expires. Điều này được xác định trong RFC 7234: “Nếu phản hồi bao gồm trường Cache-Control với chỉ thị max-age, người nhận PHẢI bỏ qua trường Expires.” Trong thực tế, khuyên không nên trả về Expires cho các máy khách hiện đại, vì Cache-Control bao phủ tất cả các kịch bản của Expires. Tuy nhiên, để tương thích ngược với các proxy và trình duyệt cũ, cả hai tiêu đề có thể được trả về.

Expires tồn tại chủ yếu cho nội dung tĩnh trên Nginx và Apache — các máy chủ này tự động thêm cả hai tiêu đề. Nếu dự án của bạn gặp Expires mà không có Cache-Control, hãy thay thế nó bằng Cache-Control với max-age: độ chính xác kiểm soát bộ nhớ đệm được cải thiện và sự phụ thuộc vào múi giờ được loại bỏ. Để di chuyển, chỉ cần cấu hình máy chủ để thêm Cache-Control thay vì Expires.

nginx
# Nginx: Cache-Control cho tệp tĩnh
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 30d;
    add_header Cache-Control "public, immutable, max-age=2592000";
}

# Các chính sách khác nhau cho các loại nội dung khác nhau
location /api/config {
    expires -1;
    add_header Cache-Control "no-cache, must-revalidate";
}

location /api/static-data {
    expires 5m;
    add_header Cache-Control "public, max-age=300";
}

Trong cấu hình Nginx, các tệp tĩnh (CSS, JS, hình ảnh) được đặt Cache-Control trong 30 ngày với thuộc tính immutable — thuộc tính này thông báo cho trình duyệt rằng tài nguyên không bao giờ thay đổi ở URL này (quản lý phiên bản thông qua hash trong tên tệp). Các điểm cuối API sử dụng no-cache cho dữ liệu động và public với max-age ngắn cho dữ liệu tham khảo — danh sách thường xuyên được yêu cầu và hiếm khi thay đổi.

Bộ nhớ đệm trong ứng dụng di động

Trong các ứng dụng di động, Cache-Control đóng một vai trò đặc biệt do các hạn chế của mạng di động: độ trễ cao, kết nối không ổn định, giới hạn lưu lượng. Bộ nhớ đệm thích hợp cho phép hiển thị dữ liệu cho người dùng ngay lập tức, ngay cả khi ngoại tuyến, và cập nhật chúng trong nền. OkHttp trên Android và URLSession trên iOS có hệ thống bộ nhớ đệm tích hợp tôn trọng Cache-Control.

OkHttp sử dụng CacheInterceptor, đọc Cache-Control từ phản hồi và tự động quản lý bộ nhớ đệm. Nếu máy chủ trả về Cache-Control: max-age=3600, OkHttp sẽ không gửi yêu cầu đến máy chủ trong một giờ. Sau khi max-age hết hạn, OkHttp gửi một yêu cầu có điều kiện với If-Modified-Since và If-None-Match. Cấu hình bộ nhớ đệm trong OkHttp: OkHttpClient.Builder().cache(Cache(directory, maxSize)).

kotlin
fun createCachedClient(cacheDir: File): OkHttpClient {
    return OkHttpClient.Builder()
        .cache(Cache(cacheDir, 10L * 1024 * 1024))
        .addNetworkInterceptor { chain ->
            val response = chain.proceed(chain.request())
            response.newBuilder()
                .header("Cache-Control",
                    "public, max-age=300")
                .removeHeader("Pragma")
                .build()
        }
        .build()
}

Mã tạo một OkHttpClient với bộ nhớ đệm 10 MB và ghi đè Cache-Control thông qua NetworkInterceptor. Nếu máy chủ không trả về Cache-Control hoặc sử dụng Expires, bộ chặn sẽ thêm public, max-age=300 (5 phút). Bộ chặn loại bỏ tiêu đề Pragma cũ (HTTP/1.0) để tương thích. Bộ nhớ đệm trên iOS hoạt động tương tự thông qua URLCache.shared với các cài đặt memoryCapacity và diskCapacity.

Chế độ ngoại tuyến và stale-while-revalidate

Chỉ thị stale-while-revalidate cho phép hiển thị bộ nhớ đệm cũ cho người dùng trong khi ứng dụng tải dữ liệu mới trong nền. Điều này mang lại hiệu ứng phản hồi tức thì: người dùng thấy nội dung ngay lập tức và sau một giây, nó được cập nhật lên phiên bản hiện tại. Được OkHttp hỗ trợ từ phiên bản 3.10 và URLCache trên iOS 14+. Ví dụ: Cache-Control: max-age=3600, stale-while-revalidate=300 — 1 giờ bộ nhớ đệm mới, sau đó 5 phút hiển thị dữ liệu cũ với làm mới nền.

Ví dụ cấu hình Cache-Control

Các loại tài nguyên khác nhau yêu cầu các chiến lược bộ nhớ đệm khác nhau. Hãy xem các cấu hình tối ưu cho các kịch bản điển hình trong phát triển di động. Đối với nội dung tĩnh có hash trong tên tệp (bundle.abc123.js), bạn có thể đặt max-age lên đến 1 năm với immutable. Đối với danh sách API hiếm khi được cập nhật (thư mục, danh mục) — max-age từ 5 phút đến 1 giờ với stale-while-revalidate.

Loại tài nguyênCache-ControlGiải thích
Tài sản tĩnh có phiên bảnpublic, max-age=31536000, immutable1 năm, tệp không thay đổi (hash trong URL)
Tài sản tĩnh không có phiên bảnpublic, max-age=86400, must-revalidate1 ngày với xác minh lại bắt buộc sau đó
API: dữ liệu tham khảopublic, max-age=600, stale-while-revalidate=6010 phút bộ nhớ đệm + 1 phút cũ
API: dữ liệu người dùngprivate, max-age=601 phút, chỉ cho một người dùng cụ thể
API: dữ liệu nhạy cảmno-storeCấm hoàn toàn bộ nhớ đệm
Trang HTMLno-cache, must-revalidateXác minh ở mỗi yêu cầu, 304 nếu không thay đổi

Điều quan trọng là phải nhớ về bảo mật: đối với các phản hồi chứa dữ liệu cá nhân của người dùng, luôn đặt private. Nếu không có chỉ thị này, một proxy công cộng (ví dụ: doanh nghiệp) có thể lưu trữ đệm phản hồi và chuyển nó cho người dùng khác. Đối với token xác thực và thông tin thanh toán, hãy sử dụng no-store — ngay cả bộ nhớ đệm riêng cũng không nên lưu dữ liệu này trên đĩa.

Gỡ lỗi bộ nhớ đệm

Để xác minh tính đúng đắn của Cache-Control, hãy sử dụng tiêu đề Age (bộ nhớ đệm đã được lưu trữ bao nhiêu giây) và X-Cache (hit/miss trên CDN). Trong trình duyệt — tab Network, cột Size hiển thị “from disk cache” hoặc “304 Not Modified”. Nếu một tài nguyên đáng lẽ phải được lưu trữ đệm nhưng lại tải mỗi lần, hãy kiểm tra xem máy chủ có thêm Cache-Control: no-cache hoặc Pragma: no-cache cùng với các chỉ thị của bạn không.

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

Sự khác biệt giữa max-age và s-maxage là gì?

max-age áp dụng cho tất cả bộ nhớ đệm (bao gồm trình duyệt), s-maxage chỉ áp dụng cho bộ nhớ đệm dùng chung (proxy, CDN). Nếu s-maxage được chỉ định, CDN sẽ bỏ qua max-age và sử dụng s-maxage. Điều này cho phép đặt thời gian sống khác nhau cho trình duyệt và CDN.

Có thể hủy bỏ bộ nhớ đệm sau khi gửi Cache-Control không?

Không, sau khi gửi phản hồi với max-age, máy khách sẽ không gửi yêu cầu cho đến khi hết thời gian. Để vô hiệu hóa bộ nhớ đệm ngay lập tức, bạn cần thay đổi URL của tài nguyên (thêm phiên bản/hash) và gửi thông báo push hoặc tin nhắn WebSocket để đặt lại bắt buộc.

Chỉ thị immutable là gì?

Chỉ thị immutable (RFC 8246) thông báo cho trình duyệt rằng tài nguyên sẽ không bao giờ thay đổi ở URL này. Trình duyệt thậm chí không cố gửi yêu cầu có điều kiện khi làm mới trang — nó sử dụng bộ nhớ đệm cho đến khi max-age hết hạn. Chỉ hoạt động với các tệp có phiên bản.

Cache-Control ảnh hưởng đến SEO như thế nào?

Googlebot tính đến Cache-Control: bộ nhớ đệm dài giúp tăng tốc độ thu thập dữ liệu lặp lại. noindex với bộ nhớ đệm nhanh được chấp nhận. no-store có thể làm chậm quá trình lập chỉ mục vì Googlebot sẽ tải trang từ đầu mỗi lần. max-age quá ngắn làm tăng tải máy chủ trong quá trình thu thập dữ liệu.

Làm thế nào để cấu hình Cache-Control trong Express.js?

Thông qua helmet hoặc middleware: res.set('Cache-Control', 'public, max-age=3600'). Đối với tệp tĩnh, hãy sử dụng express.static với tham số maxAge: express.static('public', {maxAge: '1y'}). Đối với các tuyến đường động — riêng lẻ trong từng trình xử lý.

Tổng kết

  • Cache-Control — tiêu đề HTTP chính để quản lý bộ nhớ đệm với hệ thống chỉ thị linh hoạt
  • max-age — thời gian sống tính bằng giây kể từ phản hồi; chỉ thị chính cho tất cả các kịch bản bộ nhớ đệm
  • private vs public — private chỉ cho máy khách, public cho proxy và CDN; ảnh hưởng đến bảo mật dữ liệu
  • no-cache yêu cầu xác minh, no-store cấm hoàn toàn bộ nhớ đệm; mục đích khác nhau, đừng nhầm lẫn
  • s-maxage — ghi đè max-age cho bộ nhớ đệm dùng chung, hữu ích để phân chia chính sách trình duyệt/CDN
  • stale-while-revalidate — hiển thị bộ nhớ đệm cũ với làm mới nền để có UX tức thì
  • Khuyến nghị — cấu hình Cache-Control cho từng loại tài nguyên trên máy chủ và trong máy khách HTTP di động

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm