Dùng Bộ nhớ đệm HTTP để ngăn các yêu cầu không cần thiết về mạng

Việc tìm nạp các tài nguyên qua mạng vừa chậm vừa tốn kém:

  • Phản hồi lớn yêu cầu nhiều lượt trả về giữa trình duyệt và máy chủ.
  • Trang của bạn sẽ không tải cho đến khi tất cả tài nguyên quan trọng của trang đã được tải xuống hoàn toàn.
  • Nếu ai đó truy cập trang web của bạn bằng gói dữ liệu di động hạn chế, thì mọi yêu cầu kết nối mạng không cần thiết đều là lãng phí tiền bạc.

Làm cách nào để tránh các yêu cầu mạng không cần thiết? Bộ nhớ đệm HTTP của trình duyệt là tuyến phòng vệ đầu tiên. Đây không nhất thiết là phương pháp mạnh mẽ hoặc linh hoạt nhất và bạn có quyền kiểm soát hạn chế trong thời gian tồn tại của các phản hồi được lưu vào bộ nhớ đệm, nhưng cách này hiệu quả, được hỗ trợ trong mọi trình duyệt và không đòi hỏi nhiều việc.

Hướng dẫn này trình bày các thông tin cơ bản về việc triển khai bộ nhớ đệm HTTP hiệu quả.

Khả năng tương thích với trình duyệt

Thực ra không có API duy nhất nào có tên là Bộ nhớ đệm HTTP. Đây là tên chung cho một tập hợp các API nền tảng web. Những API đó được hỗ trợ trong tất cả trình duyệt:

Cách hoạt động của Bộ nhớ đệm HTTP

Tất cả yêu cầu HTTP mà trình duyệt đưa ra trước tiên sẽ được chuyển đến bộ nhớ đệm của trình duyệt để kiểm tra xem có phản hồi nào hợp lệ vào bộ nhớ đệm có thể dùng để thực hiện yêu cầu hay không. Nếu trùng khớp, phản hồi sẽ được đọc từ bộ nhớ đệm, qua đó loại bỏ cả độ trễ mạng và chi phí dữ liệu phát sinh trong quá trình chuyển.

Hành vi của Bộ nhớ đệm HTTP chịu sự kiểm soát của tổ hợp tiêu đề yêu cầutiêu đề phản hồi. Trong trường hợp lý tưởng nhất, bạn sẽ có quyền kiểm soát cả mã cho ứng dụng web (sẽ xác định tiêu đề yêu cầu) và cấu hình máy chủ web của bạn (sẽ xác định tiêu đề phản hồi).

Hãy xem bài viết về Bộ nhớ đệm HTTP của MDN để biết thông tin tổng quan chuyên sâu hơn về khái niệm.

Tiêu đề yêu cầu: giữ nguyên tiêu đề mặc định (thường)

Tuy có một số tiêu đề quan trọng cần được đưa vào các yêu cầu gửi đi của ứng dụng web, nhưng trình duyệt hầu như luôn thay mặt bạn đặt những tiêu đề này khi đưa ra yêu cầu. Các tiêu đề của yêu cầu ảnh hưởng đến việc kiểm tra độ mới, chẳng hạn như If-None-MatchIf-Modified-Since sẽ chỉ xuất hiện dựa trên hiểu biết của trình duyệt về các giá trị hiện tại trong Bộ nhớ đệm HTTP.

Đây là tin tốt – có nghĩa là bạn có thể tiếp tục đưa các thẻ như <img src="my-image.png"> vào HTML của mình và trình duyệt sẽ tự động xử lý việc lưu vào bộ nhớ đệm HTTP cho bạn mà không mất thêm công sức.

Tiêu đề phản hồi: định cấu hình máy chủ web của bạn

Phần quan trọng nhất trong quá trình thiết lập bộ nhớ đệm HTTP là tiêu đề mà máy chủ web của bạn thêm vào mỗi phản hồi gửi đi. Các tiêu đề sau đây đều ảnh hưởng đến hoạt động lưu vào bộ nhớ đệm hiệu quả:

  • Cache-Control. Máy chủ có thể trả về một lệnh Cache-Control để chỉ định cách thức và thời gian mà trình duyệt và các bộ nhớ đệm trung gian khác sẽ lưu phản hồi riêng lẻ vào bộ nhớ đệm.
  • ETag. Khi tìm thấy một phản hồi đã hết hạn vào bộ nhớ đệm, trình duyệt có thể gửi một mã thông báo nhỏ (thường là hàm băm của nội dung tệp) đến máy chủ để kiểm tra xem tệp đã thay đổi hay chưa. Nếu máy chủ trả về cùng một mã thông báo, thì tệp sẽ giữ nguyên và bạn không cần tải xuống lại.
  • Last-Modified. Tiêu đề này có mục đích tương tự như ETag, nhưng sử dụng chiến lược dựa trên thời gian để xác định xem tài nguyên đã thay đổi hay chưa, chứ không phải chiến lược dựa trên nội dung của ETag.

Một số máy chủ web có tính năng hỗ trợ tích hợp sẵn để đặt các tiêu đề đó theo mặc định, trong khi một số máy chủ khác loại bỏ hoàn toàn tiêu đề trừ phi bạn định cấu hình các tiêu đề đó một cách rõ ràng. Thông tin chi tiết cụ thể về cách định cấu hình tiêu đề sẽ thay đổi đáng kể tuỳ thuộc vào máy chủ web mà bạn sử dụng. Bạn nên tham khảo tài liệu của máy chủ đó để có được thông tin chi tiết chính xác nhất.

Để tiết kiệm một số công sức tìm kiếm, dưới đây là hướng dẫn về cách định cấu hình một số máy chủ web phổ biến:

Việc bỏ qua tiêu đề phản hồi Cache-Control sẽ không tắt tính năng lưu vào bộ nhớ đệm của HTTP! Thay vào đó, các trình duyệt sẽ đoán chính xác loại hành vi lưu vào bộ nhớ đệm phù hợp nhất đối với một loại nội dung nhất định. Có khả năng bạn muốn có nhiều quyền kiểm soát hơn những gì mang lại, vì vậy, hãy dành thời gian để định cấu hình các tiêu đề phản hồi của bạn.

Bạn nên sử dụng giá trị tiêu đề phản hồi nào?

Có 2 trường hợp quan trọng mà bạn nên đề cập khi định cấu hình tiêu đề phản hồi của máy chủ web.

Lưu vào bộ nhớ đệm tồn tại trong thời gian dài cho các URL được tạo phiên bản

Cách URL được tạo phiên bản có thể giúp ích cho chiến lược lưu vào bộ nhớ đệm của bạn
Bạn nên sử dụng URL đã tạo phiên bản vì chúng giúp vô hiệu hoá các phản hồi đã lưu vào bộ nhớ đệm một cách dễ dàng hơn.

Giả sử máy chủ của bạn hướng dẫn trình duyệt lưu tệp CSS vào bộ nhớ đệm trong 1 năm (Cache-Control: max-age=31536000), nhưng nhà thiết kế của bạn vừa thực hiện một bản cập nhật khẩn cấp và bạn cần triển khai ngay lập tức. Làm cách nào để bạn thông báo cho trình duyệt cập nhật bản sao "lỗi thời" của tệp trong bộ nhớ đệm? Bạn không thể làm thay đổi URL của tài nguyên. Sau khi trình duyệt lưu phản hồi vào bộ nhớ đệm, phiên bản đã lưu vào bộ nhớ đệm sẽ được sử dụng cho đến khi không còn mới, như được max-age hoặc expires xác định, hoặc cho đến khi bị loại khỏi bộ nhớ đệm vì một lý do nào đó, ví dụ: người dùng xoá bộ nhớ đệm của trình duyệt. Kết quả là, có những người dùng khác nhau có thể sẽ sử dụng các phiên bản khác nhau của tệp khi trang được tạo: người dùng vừa tìm nạp tài nguyên sẽ sử dụng phiên bản mới, trong khi những người dùng đã lưu vào bộ nhớ đệm một bản sao cũ (nhưng vẫn hợp lệ) sử dụng phiên bản phản hồi cũ hơn. Làm cách nào để bạn tận dụng được cả hai khía cạnh: lưu vào bộ nhớ đệm phía máy khách và cập nhật nhanh? Bạn thay đổi URL của tài nguyên và buộc người dùng tải phản hồi mới xuống mỗi khi nội dung tương ứng thay đổi. Thông thường, bạn thực hiện việc này bằng cách nhúng vân tay số của tệp hoặc số phiên bản của tệp vào tên tệp — ví dụ: style.x234dff.css.

Khi phản hồi các yêu cầu về URL chứa "vân tay" hoặc thông tin phiên bản và nội dung không bao giờ thay đổi, hãy thêm Cache-Control: max-age=31536000 vào phản hồi của bạn.

Việc đặt giá trị này cho trình duyệt biết rằng khi cần tải cùng một URL bất cứ lúc nào trong 1 năm tới (31.536.000 giây; giá trị tối đa được hỗ trợ), trình duyệt có thể sử dụng ngay giá trị đó trong Bộ nhớ đệm HTTP mà không cần phải gửi yêu cầu mạng tới máy chủ web của bạn. Thật tuyệt! Bạn sẽ ngay lập tức đạt được độ tin cậy và tốc độ nhờ tính năng tránh mạng!

Các công cụ tạo như webpack có thể tự động hoá quy trình chỉ định vân tay băm cho URL thành phần của bạn.

Xác thực lại máy chủ cho URL chưa được tạo phiên bản

Rất tiếc, không phải tất cả URL bạn tải đều được tạo phiên bản. Có thể bạn không bao gồm bước tạo bản dựng trước khi triển khai ứng dụng web, vì vậy bạn không thể thêm hàm băm vào URL nội dung của mình. Và mọi ứng dụng web đều cần tệp HTML — những tệp đó (hầu như!) sẽ không bao giờ bao gồm thông tin phiên bản, vì sẽ không ai muốn sử dụng ứng dụng web của bạn nếu họ cần nhớ rằng URL cần truy cập là https://example.com/index.34def12.html. Vậy bạn có thể làm gì với những URL đó?

Đây là một tình huống mà bạn cần phải thừa nhận thất bại. Chỉ riêng việc lưu vào bộ nhớ đệm HTTP là không đủ hiệu quả để tránh hoàn toàn mạng. (Đừng lo, bạn sẽ sớm tìm hiểu về service worker, cung cấp sự hỗ trợ mà chúng ta cần để thiết lập lại cuộc chiến có lợi cho bạn.) Tuy nhiên, bạn có thể thực hiện một vài bước để đảm bảo rằng các yêu cầu mạng sẽ nhanh chóng và hiệu quả nhất có thể.

Các giá trị Cache-Control sau đây có thể giúp bạn tinh chỉnh vị trí và cách thức lưu các URL chưa được tạo phiên bản vào bộ nhớ đệm:

  • no-cache. Thao tác này hướng dẫn trình duyệt rằng trình duyệt phải xác thực lại với máy chủ mỗi lần sử dụng trước khi sử dụng phiên bản URL đã lưu vào bộ nhớ đệm.
  • no-store. Thao tác này sẽ hướng dẫn trình duyệt và các bộ nhớ đệm trung gian khác (như CDN) không bao giờ lưu trữ bất kỳ phiên bản nào của tệp.
  • private. Các trình duyệt có thể lưu tệp vào bộ nhớ đệm nhưng bộ nhớ đệm trung gian thì không.
  • public. Phản hồi có thể được bất kỳ bộ nhớ đệm nào lưu trữ.

Hãy xem Phụ lục: lưu đồ quy trình Cache-Control để trực quan hoá quy trình quyết định(các) giá trị Cache-Control sẽ sử dụng. Ngoài ra, xin lưu ý rằng Cache-Control có thể chấp nhận danh sách các lệnh được phân tách bằng dấu phẩy. Hãy xem Phụ lục: Ví dụ về Cache-Control.

Ngoài ra, việc đặt thêm một trong hai tiêu đề phản hồi bổ sung cũng có thể giúp ích: ETag hoặc Last-Modified. Như đã đề cập trong phần Tiêu đề phản hồi, ETagLast-Modified đều phục vụ cùng một mục đích: xác định xem trình duyệt có cần tải lại tệp đã lưu vào bộ nhớ đệm đã hết hạn hay không. ETag là phương pháp được đề xuất vì phương pháp này chính xác hơn.

Ví dụ về ETag

Giả sử đã 120 giây kể từ lần tìm nạp đầu tiên và trình duyệt đã khởi tạo một yêu cầu mới cho cùng một tài nguyên. Trước tiên, trình duyệt sẽ kiểm tra Bộ nhớ đệm HTTP và tìm phản hồi trước đó. Rất tiếc, trình duyệt không thể sử dụng phản hồi trước đó vì phản hồi hiện đã hết hạn. Tại thời điểm này, trình duyệt có thể gửi một yêu cầu mới và tìm nạp phản hồi đầy đủ mới. Tuy nhiên, cách này không hiệu quả vì nếu tài nguyên không thay đổi, thì không có lý do gì để tải chính thông tin đã có trong bộ nhớ đệm xuống! Đó là vấn đề mà mã thông báo xác thực được thiết kế để giải quyết như đã chỉ định trong tiêu đề ETag. Máy chủ sẽ tạo và trả về một mã thông báo tuỳ ý, thường là một hàm băm hoặc một số dấu vân tay khác trong nội dung của tệp. Trình duyệt không cần biết cách tạo vân tay số mà chỉ cần gửi vân tay đó đến máy chủ trong yêu cầu tiếp theo. Nếu vân tay số vẫn giữ nguyên, thì tài nguyên đó không thay đổi và trình duyệt có thể bỏ qua quá trình tải xuống.

Bằng cách đặt ETag hoặc Last-Modified, bạn sẽ giúp yêu cầu xác thực lại hiệu quả hơn nhiều. Chúng sẽ kích hoạt các tiêu đề yêu cầu If-Modified-Since hoặc If-None-Match đã được đề cập trong Tiêu đề yêu cầu.

Khi một máy chủ web được định cấu hình đúng cách nhìn thấy các tiêu đề của yêu cầu đến, máy chủ có thể xác nhận xem phiên bản của tài nguyên mà trình duyệt đã có trong Bộ nhớ đệm HTTP có khớp với phiên bản mới nhất trên máy chủ web hay không. Nếu có kết quả trùng khớp, máy chủ có thể phản hồi bằng phản hồi HTTP 304 Not Modified, tương đương với "Ok, hãy tiếp tục sử dụng những gì bạn đã có!" Có rất ít dữ liệu cần chuyển khi gửi loại phản hồi này, vì vậy, việc này thường nhanh hơn nhiều so với việc thực sự gửi lại bản sao tài nguyên thực tế đang được yêu cầu.

Sơ đồ về ứng dụng yêu cầu tài nguyên và máy chủ phản hồi bằng tiêu đề 304.
Trình duyệt yêu cầu /file từ máy chủ và bao gồm tiêu đề If-None-Match để hướng dẫn máy chủ chỉ trả về tệp đầy đủ nếu ETag của tệp trên máy chủ không khớp với giá trị If-None-Match của trình duyệt. Trong trường hợp này, 2 giá trị trùng khớp nhau nên máy chủ sẽ trả về phản hồi 304 Not Modified kèm theo hướng dẫn về thời gian lưu tệp vào bộ nhớ đệm (Cache-Control: max-age=120).

Tóm tắt

Bộ nhớ đệm HTTP là một cách hiệu quả để cải thiện hiệu suất tải vì bộ nhớ đệm này giảm các yêu cầu mạng không cần thiết. Tính năng này được hỗ trợ trên tất cả các trình duyệt và không mất quá nhiều công sức để thiết lập.

Các cấu hình Cache-Control sau đây là một khởi đầu tốt:

  • Cache-Control: no-cache cho các tài nguyên cần được xác thực lại với máy chủ trước mỗi lần sử dụng.
  • Cache-Control: no-store cho các tài nguyên không bao giờ được lưu vào bộ nhớ đệm.
  • Cache-Control: max-age=31536000 cho các tài nguyên được tạo phiên bản.

Tiêu đề ETag hoặc Last-Modified có thể giúp bạn xác thực lại các tài nguyên bộ nhớ đệm đã hết hạn một cách hiệu quả hơn.

Tìm hiểu thêm

Nếu bạn muốn tìm hiểu sâu hơn những kiến thức cơ bản về cách sử dụng tiêu đề Cache-Control, hãy xem hướng dẫn Các phương pháp hay nhất để lưu vào bộ nhớ đệm và hướng dẫn về Gotchas tuổi tối đa của Jake Archibald.

Hãy xem bài viết Yêu thích bộ nhớ đệm để biết hướng dẫn về cách tối ưu hoá mức sử dụng bộ nhớ đệm cho khách truy cập cũ.

Phụ lục: Các mẹo khác

Nếu có thêm thời gian, bạn có thể tham khảo thêm các cách sau đây để tối ưu hoá việc sử dụng Bộ nhớ đệm HTTP:

  • Sử dụng URL nhất quán. Nếu bạn phân phát cùng một nội dung trên nhiều URL, thì nội dung đó sẽ được tìm nạp và lưu trữ nhiều lần.
  • Giảm thiểu tỷ lệ người dùng rời bỏ. Nếu một phần tài nguyên (chẳng hạn như tệp CSS) cập nhật thường xuyên, trong khi phần còn lại của tệp thì không (chẳng hạn như mã thư viện), hãy cân nhắc chia mã cập nhật thường xuyên thành một tệp riêng và sử dụng chiến lược lưu vào bộ nhớ đệm trong thời gian ngắn đối với mã cập nhật thường xuyên và chiến lược thời lượng lưu vào bộ nhớ đệm dài đối với mã không thường xuyên thay đổi.
  • Hãy xem lệnh stale-while-revalidate mới nếu chính sách Cache-Control của bạn có thể chấp nhận được một mức độ lỗi thời nào đó.

Phụ lục: lưu đồ quy trình Cache-Control

Sơ đồ quy trình

Phụ lục: Cache-Control ví dụ

Giá trị Cache-Control Giải thích
max-age=86400 Phản hồi có thể được các trình duyệt và bộ nhớ đệm trung gian lưu vào bộ nhớ đệm trong tối đa 1 ngày (60 giây x 60 phút x 24 giờ).
private, max-age=600 Phản hồi có thể được trình duyệt lưu vào bộ nhớ đệm (chứ không phải bộ nhớ đệm trung gian) trong tối đa 10 phút (60 giây x 10 phút).
public, max-age=31536000 Bất kỳ bộ nhớ đệm nào cũng có thể lưu trữ phản hồi này trong 1 năm.
no-store Phản hồi này không được phép lưu vào bộ nhớ đệm và phải được tìm nạp đầy đủ trong mỗi yêu cầu.