HTTP Caching là gì?

Khi ai đó ghé thăm website, thì mọi thứ mà trang web cần dùng để hiển thị và hoạt động phải lấy từ một số nguồn nào đấy. Tất cả văn bản, ảnh, style CSS, JavaScript, và các file đa phương tiện phải được trình duyệt tải về để hiển thị hoặc thực thi. Bạn có thể cho trình duyệt lựa chọn nơi mà nó có thể lấy tài nguyên về, và điều ấy có thể tạo ra sự khác biệt lớn trong tốc độ tải trang của bạn.

Khi lần đầu tiên trình duyệt tải một trang web, nó lưu trữ các tài nguyên của trang trong HTTP Cache. Vào lần kế tiếp, khi trình duyệt tải trang đó về, nó có thể tìm kiếm trong cache các tài nguyên mà nó đã tìm nạp trước đó (previously fetched), và nhận dữ liệu từ ổ cứng thiết bị của người dùng (from disk), điều này thường cho tốc độ nhanh hơn so với việc phải tải chúng về qua mạng internet (networks).

Mặc dù HTTP caching đã được chuẩn hóa theo thông số kỹ thuật của IETF (Internet Engineerring Task Force), các trình duyệt có thể có nhiều bộ nhớ đệm cache khác nhau dựa trên cách nó tìm nạp, lưu trữ và giữ lại nội dung. Bạn có thể tìm hiểu sự khác nhau giữa các biến thể cache này trong bài viết rất chất lượng sau (tiếng Anh): Câu chuyện về bốn bộ nhớ cache.

Tất nhiên, mọi lần ghé thăm đầu tiên đến trang của bạn sẽ chẳng có bất cứ tài nguyên nào được cache trước đó cả (trừ tài nguyên của bên thứ ba). Thậm chí lượt ghé thăm lặp lại có thể chẳng có nhiều tài nguyên trong HTTP cache; họ (người dùng) có thể đã xóa nó theo cách thủ công, hoặc thiết lập trình duyệt thực hiện điều đó tự động, hoặc chủ động ép trình duyệt phải tải trang tươi mới (fresh page load) với kết hợp control-key. Tuy nhiên, vẫn có một số lượng đáng kể người dùng của bạn có thể ghé thăm lại trang với ít nhiều tài nguyên đã được cache sẵn rồi, và điều đó có thể tạo ra sự khác biệt lớn trong thời gian tải trang (load time). Tối đa hóa việc sử dụng cache là điều vô cùng quan trọng nếu bạn muốn tăng tốc cho những lượt truy cập quay trở lại website (return visit, không chỉ riêng trang cụ thể được truy cập trước đó, truy cập trên các trang khác trên website mà trang này có chung tài nguyên với trang trước đó đều có khả năng giúp bạn cải thiện tốc độ trang).

Bật caching

Bộ nhớ caching hoạt động bằng cách phân loại các loại tài nguyên nhất định trên trang theo khía cạnh tần suất thay đổi thường xuyên hay không thường xuyên của chúng. Ví dụ ảnh logo có thể không bao giờ thay đổi, nhưng các đoạn mã JavaScript có thể thay đổi sau vài ngày. Việc xác định rõ kiểu nội dung nào tĩnh (static) nhiều hơn và kiểu nội dung nào động (dynamic) nhiều hơn sẽ đem lại lợi ích cho cả bạn và người dùng.

Điều quan trọng cần phải nhớ là những gì chúng ta nghĩ về bộ nhớ đệm trình duyệt trên thực tế có thể diễn ra tại bất cứ điểm dừng trung gian nào nằm giữa máy chủ gốc và trình duyệt phía người dùng, chẳng hạn như proxy cache hoặc cache của mạng phân tán nội dung (CDN).

Cache header

Có hai kiểu cache header chính, gồm cache-control expires, giúp xác định nhiều đặc tính caching cho các nguồn tài nguyên của bạn. Thường thì, cache-control được coi là cách tiếp cận hiện đại và mềm dẻo hơn so với expires, nhưng cả hai header có thể sử dụng đồng thời.

Cache header được áp dụng vào các tài nguyên ở cấp độ server – lấy ví dụ, trong file .htaccess để thiết lập các đặc tính caching trên máy chủ Apache (nền tảng được sử dụng bởi gần một nửa các website đang hoạt động ngày nay). Caching được bật bằng cách xác định một tài nguyên cụ thể hoặc một kiểu tài nguyên, chẳng hạn như ảnh hoặc CSS, rồi sau đó chỉ định header cho các tài nguyên này với tùy chọn caching mà bạn muốn.

Cache-control

Bạn có thể bật cache-control với nhiều tùy chọn trong danh sách được phân cách bằng dấu phẩy (comma-delimited list). Dưới đây là ví dụ khi tùy chỉnh cho .htaccess của Apache nhằm thiết lập caching (set Cache-Control) cho nhiều định dạng ảnh khác nhau, khớp với (filesMatch) danh sách đuôi mở rộng (jpg, png, jpeg, ico, gif), với thời gian là một tháng (max-age=2592000) và truy cập công khai (public), một số tùy chọn khác sẽ được thảo luận bên dưới. P/S

<filesMatch ".(ico|jpg|jpeg|png|gif)$">
 Header set Cache-Control "max-age=2592000, public"
</filesMatch>

Ví dụ tiếp theo thiết lập caching cho style (css) và script (js), đây là các tài nguyên có khả năng thay đổi nhiều hơn so với ảnh, do vậy tôi thiết lập thời gian caching (set Cache-Control) chỉ một ngày (max-age=86400) và cũng truy cập công khai (public):

<filesMatch ".(css|js)$">
 Header set Cache-Control "max-age=86400, public"
</filesMatch>

Cache-Control có một số tùy chọn, thường được gọi là các chỉ thị (directives), cái có thể được thiết lập để đưa ra chỉ dẫn cụ thể về cách các yêu cầu cache được xử lý như thế nào. Một số chỉ thị phổ biến được giải thích bên dưới; bạn có thể tìm thêm thông tin trên mạng để hiểu rõ hơn, một nguồn đáng tin cậy là: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control (của nhóm phát triển trình duyệt Mozilla).

  • no-cache: Từ này dễ gây hiểu lầm, trong thực tế nó chỉ định nội dung có thể được cache, nhưng khi ấy nó phải được xác thực lại trong từng yêu cầu trước khi được phục vụ cho khách truy cập. Điều này sẽ ép máy khách phải tải nội dung tươi mới nếu cần, nhưng nó tránh cho máy khách đỡ phải tải lại tài nguyên nếu tài nguyên đã được cache không thay đổi nội dung. Thuộc tính no-cache đối lập, loại trừ với no-store.
  • no-store: Chỉ ra rằng nội dung thực sự không thể được cache theo bất cứ cách nào thông qua bất cứ nền tảng cache chính hoặc trung gian nào. Đây là tùy chọn tốt cho các tài nguyên lưu trữ dữ liệu nhạy cảm (sensitive data), hoặc cho các tài nguyên mà gần như chắc chắn thay đổi từ truy cập này sang truy cập khác. Thuộc tính no-store đối lập, loại trừ no-cache.
  • public: Chỉ ra rằng nội dung có thể được cache bởi trình duyệt và bất cứ nền tảng cache trung gian nào. Nó ghi đè lên cài đặt mặc định private cho các yêu cầu sử dụng xác thực HTTP. Thuộc tính public trái ngược, loại trừ với private.
  • private: Được thiết kế để nội dung có thể lưu trữ bởi trình duyệt của người dùng, nhưng có thể không được cache bởi bất cứ nền tảng cache trung gian nào. Thường sử dụng cho người dùng cụ thể, nhưng không áp dụng lên dữ liệu quá nhạy cảm. Thuộc tính private trái ngược, loại trừ với public.
  • max-age: Định nghĩa thời gian tối đa mà nội dung có thể được cache trước khi nó phải xác nhận lại (revalidate) hoặc tải lại tài nguyên từ máy chủ gốc. Tùy chọn này thường thay thế cho header expires (đề cập bên dưới) và thiết lập giá trị tính bằng giây, với thời gian hết hạn hợp lệ tối đa là một năm (31536000 giây).

Expries caching

Bạn cũng có thể bật caching bằng cách sử dụng chỉ thị expiration, hoặc expiry, áp khung thời gian hết hạn cho một số kiểu file nhất định, cái nói cho trình duyệt biết bao lâu thì nên sử dụng tài nguyên cache trước khi yêu cầu một bản copy tươi mới nhất từ máy chủ gốc. Header expires chỉ thiết lập thời gian trong tương lai khi nội dung đến thời điểm hết hạn. Sau thời điểm đó, các yêu cầu cho nội dung phải được gọi từ máy chủ gốc (ở khía cạnh này thì cache-control tốt hơn, kể cả khi tài nguyên cache đã hết hạn, nhưng nếu trình duyệt hỏi máy chủ và nhận được câu trả lời rằng tài nguyên vẫn chưa thay đổi thì trình duyệt không phải tải dữ liệu gì, nó vẫn lấy tài nguyên từ cache). Với tùy chọn header cache-control mới và mềm dẻo hơn thì expires header thường được sử dụng trong vai trò dự phòng (fall back).

Dưới đây là ví dụ mẫu về cách bạn thiết lập caching trong file .htaccess trên máy chủ Apache (P/S: sửa file .htaccess không đúng có thể làm gián đoạn truy cập trang web, vì vậy trước hết bạn nên tải về bản dự phòng rồi mới sửa, bạn nên tham khảo trước bài hướng dẫn chỉnh sửa file .htaccess ở đây).

## EXPIRES CACHING ##
ExpiresActive On
ExpiresByType image/jpg "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/pdf "access plus 1 month"
ExpiresByType text/x-javascript "access plus 1 month"
ExpiresByType application/x-shockwave-flash "access plus 1 month"
ExpiresByType image/x-icon "access plus 1 year"
ExpiresDefault "access plus 2 days"
## EXPIRES CACHING ##

(nguồn: GTmetrix)

Như bạn có thể thấy, trong ví dụ trên, các kiểu file khác nhau có ngày hết hạn khác nhau: các ảnh sẽ không hết hạn cho đến khi được một năm sau khi truy cập/caching, trong khi JavaScript, PDF và CSS sẽ hết hạn sau một tháng, và bất kỳ kiểu file nào khác không được lượt kê rõ ràng trong danh sách trên sẽ hết hạn trong vòng hai ngày. Thời gian lưu trữ phụ thuộc vào bạn, và nên được lựa chọn dựa trên kiểu file và tần số cập nhật của bạn. Lấy ví dụ, nếu bạn thường xuyên thay đổi CSS, bạn có thể muốn áp dụng thời gian hết hạn ngắn hơn, hoặc thậm chí chẳng đưa ra chỉ thị gì luôn, và để nó tự động thiết lập theo mặc định tối thiểu chỉ hai ngày. Ngược lại, nếu bạn liên kết đến một số file PDF tĩnh mà gần như chắc chắn không bao giờ thay đổi, bạn có thể muốn áp dụng thời gian hết hạn dài ngày hơn cho chúng.

Mẹo: Bạn không nên để thời gian hết hạn nhiều hơn một năm; trên intertnet, ảnh hưởng đó chẳng khác gì vô tận, và như đã lưu ý ở phần trên, một năm đã là giá trị tối đa cho max-age trong cache-control rồi, có muốn hơn cũng chẳng được.

Tổng kết

Caching là cách đáng tin cậy và không phức tạp trong việc triển khai, nó giúp cải thiện tốc độ tải trang của bạn và vì thế cả trải nghiệm của người dùng nữa. Nó đủ sức mạnh để tạo ra được các sắc thái tinh vi đa dạng cho một kiểu nội dung cụ thể, nhưng cũng đủ mềm dẻo để cho phép bạn dễ dàng cập nhật khi nội dung trên trang thay đổi.

Hãy quả quyết, tự tin khi triển khai caching, nhưng cũng cần phải ý thức được rằng nếu bạn thay đổi tài nguyên mà có thời gian lưu trữ dài, bạn có thể vô tình làm cho một số khách truy cập lại mất cơ hội tiếp cận được nội dung mới (*). Bạn có thể tham khảo thêm các thảo luận rất hay về các mẫu caching, tùy chọn và các rủi ro tiềm năng trong bài viết (tiếng Anh) Các thực hành caching tốt nhất và Max-age.

(*): Để khắc phục hiện tượng này, với các file CSS, JS thay đổi mà cache có thời gian thiết lập dài người ta sẽ chủ động đổi tên file CSS và JS để nó tải về tài nguyên mới được cập nhật thay vì tải từ thiết bị người dùng tài nguyên cache đã cũ.

P/S: Bạn nào muốn tìm hiểu sâu hơn về HTTP Caching có thể tham khảo trong link tôi vừa dẫn, nó phân tích sâu hơn về max-age, cũng như việc xác thực lại để đảm bảo người dùng có được nội dung cập nhật nhất trên website.

Ví dụ demo

Phần này tôi (người dịch) bổ sung để các bạn tiện kiểm tra, thử nghiệm.

Trang demo: https://code.kiencang.net/cache-demo/60s/cache-control-60.html

Trong đó tôi sử dụng Cache-Control có nội dung như sau:

<filesMatch ".(css|js)$">
 Header set Cache-Control "max-age=60, public"
</filesMatch>
<filesMatch ".(ico|jpg|jpeg|png)$">
 Header set Cache-Control "max-age=61, public"
</filesMatch>
<filesMatch ".(ttf)$">
 Header set Cache-Control "max-age=62, private"
</filesMatch>

Tất nhiên, bình thường thì chẳng có ai lại để max-age cho CSS, ảnh và font (.ttf) chỉ tầm có một phút như thế (lần lượt là 60, 61 và 62s), nhưng tôi cố tình tạo vậy để cho dễ quan sát.

Trong link trên bạn sẽ thấy có các kiểu tài nguyên sau:

  • CSS: có một file duy nhất (local host / host nội bộ)
  • JavaScript (js): có một file duy nhất (host bên ngoài, CDN của Google)
  • Ảnh: gồm 2 ảnh, một ảnh đầu là local host, còn ảnh background bên dưới là thuộc host bên ngoài
  • Font: gồm 2 font định dạng .ttf, cũng local host

Trong đó chỉ có file .ttf là được thiết lập private, còn lại đều public.

Khi kiểm tra bằng DevTools cho kết quả như sau:

Với file Font:

Cache Control max-age của TTF

Với file CSS:

Cache Control max-age của CSS

Với ảnh đầu (local host):

ảnh local host max-age Cache Control

Như vậy tất cả đều khớp với cài đặt của mã mẫu. Tuy nhiên với các tài nguyên được host bên ngoài, các thuộc tính Cache-Control của nó không chịu ảnh hưởng bởi đoạn mã ở trên mà được quyết định bởi host của bên thứ ba đó:

Với jQuery (.js):

Cache-Control max-age jQuery

Với ảnh nền (.png):

cache control max-age của ảnh nền

Khi tôi tải lại trang, tôi thấy trang lấy tài nguyên từ bộ nhớ cache:

Trang lấy tài nguyên lưu trong cache

Vài phút sau tôi thực hiện tải lại, những tài nguyên có thời gian cache ngắn (khoảng 1 phút) đã hết hạn và nó sẽ lấy lại tài nguyên đó(*), trong khi tài nguyên có thời gian cache dài thì vẫn lấy dữ liệu từ bộ nhớ cache:

(*): Như đã nói ở phần trên, với các tài nguyên hết hạn max-age thì trình duyệt sẽ yêu cầu máy chủ xác nhận lại rằng tài nguyên đó có thay đổi gì không? Nếu có thay đổi thì trình duyệt mới tải tài nguyên đó về, còn nếu không nó vẫn lấy tài nguyên từ cache, và đồng thời gia hạn thời gian hết hạn. Mã trạng thái 304 được trả về đối với các tài nguyên đã hết “max-age” nhưng máy chủ gốc thông báo là không có thay đổi gì với tài nguyên đó cả so với tài nguyên đang được cache sẵn.

So sánh thời gian tải khi có cache và không có cache

Khi KHÔNG cache:

Khi không có cache

Khi cache:

Khi có cache

Các chỉ số Transferred, Finish, DOM và Load khi CÓ cache tốt hơn nhiều so với KHÔNG có cache.

(Dịch từ bài viết HTTP Caching, tác giả: Dave Gash, trang: Google Developers)

Leave a Comment