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 tài nguyên qua mạng vừa chậm vừa tốn kém:

  • Phản hồi lớn đòi hỏi nhiều lượt khứ hồi 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 một người đang truy cập vào trang web của bạn bằng gói dữ liệu di động có giới hạn, thì mọi yêu cầu mạng không cần thiết đều gây lãng phí tiền của họ.

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 thủ đầu tiên của bạ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ế đối với thời gian hoạt động của các phản hồi được lưu vào bộ nhớ đệm, nhưng phương pháp này hiệu quả, được hỗ trợ trong tất cả trình duyệt và không đòi hỏi nhiều công sức.

Hướng dẫn này trình bày những kiến thức cơ bản về cách 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 tế là không có một API nào được gọi là Bộ nhớ đệm HTTP. Đây là tên gọi chung cho một tập hợp các API nền tảng web. Các API đó được hỗ trợ trong tất cả trình duyệt:

Cache-Control

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Source

ETag

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Source

Last-Modified

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Source

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 đều được định tuyến đến bộ nhớ đệm của trình duyệt để kiểm tra xem có phản hồi hợp lệ nào được lưu vào bộ nhớ đệm có thể dùng để thực hiện yêu cầu hay không. Nếu có kết quả trùng khớp, phản hồi sẽ được đọc từ bộ nhớ đệm, giúp loại bỏ cả độ trễ mạng và chi phí dữ liệu mà quá trình chuyển dữ liệu gây ra.

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

Hãy tham khảo bài viết Lưu HTTP vào bộ nhớ đệm của MDN để biết thông tin tổng quan chi tiết hơn về khái niệm này.

Tiêu đề của yêu cầu: Giữ nguyên giá trị mặc định (thường là vậy)

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 đảm nhận việc đặt các tiêu đề đó thay cho bạn 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-Match và If-Modified-Since, sẽ xuất hiện dựa trên mức độ 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 vui, nghĩa là bạn có thể tiếp tục thêm các thẻ như <img src="my-image.png"> vào HTML 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 cần tốn thêm công sức.

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

Phần quan trọng nhất trong quá trình thiết lập lưu vào bộ nhớ đệm HTTP là các tiêu đề mà máy chủ web thêm vào mỗi phản hồi gửi đi. Tất cả các tiêu đề sau đây đều ảnh hưởng đến hành vi 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 cũng như các bộ nhớ đệm trung gian khác nên lưu phản hồi riêng lẻ vào bộ nhớ đệm.
  • ETag. Khi trình duyệt tìm thấy một phản hồi đã hết hạn trong 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 có thay đổi hay không. Nếu máy chủ trả về cùng một mã thông báo, thì tệp đó giống nhau và không cần tải lại.
  • Last-Modified. Tiêu đề này có cùng mục đích với 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 có thay đổi hay không, trái ngược với chiến lược dựa trên nội dung của ETag.

Một số máy chủ web có 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 bỏ qua hoàn toàn các tiêu đề trừ phi bạn định cấu hình rõ ràng. Thông tin chi tiết cụ thể về cách định cấu hình tiêu đề sẽ khác nhau 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ủ để biết thông tin chính xác nhất.

Để giúp bạn tiết kiệm thời gian tìm kiếm, dưới đây là hướng dẫn đị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 không vô hiệu hoá tính năng lưu vào bộ nhớ đệm HTTP! Thay vào đó, trình duyệt sẽ đoán hành vi lưu vào bộ nhớ đệm nào phù hợp nhất với một loại nội dung nhất định. Có thể bạn muốn có nhiều quyền kiểm soát hơn so với những gì được cung cấp, vì vậy, hãy dành thời gian để định cấu hình tiêu đề phản hồi.

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

Có hai 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 trong thời gian dài cho các URL có phiên bản

Cách URL có 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
Việc tạo phiên bản URL là một phương pháp hay vì giúp bạn dễ dàng vô hiệu hoá các phản hồi được lưu vào bộ nhớ đệm.

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 mà 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ưu vào bộ nhớ đệm "cũ" của tệp? Bạn không thể làm vậy, ít nhất là khi không 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 được lưu vào bộ nhớ đệm sẽ được sử dụng cho đến khi không còn mới nữa, theo xác định của max-age hoặc expires, hoặc cho đến khi bị loại bỏ khỏi bộ nhớ đệm vì một số lý do khác, chẳng hạn như người dùng xoá bộ nhớ đệm của trình duyệt. Do đó, những người dùng khác nhau có thể 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 người dùng đã lưu bản sao cũ hơn (nhưng vẫn hợp lệ) vào bộ nhớ đệm sẽ sử dụng phiên bản cũ hơn của phản hồi.

Làm cách nào để bạn có được cả hai lợi ích: 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 bất cứ khi nào nội dung của tài nguyên đó thay đổi. Thông thường, bạn có thể làm 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 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 thông tin "vân tay số" 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 sẽ cho trình duyệt biết rằng khi cần tải cùng một URL vào bất kỳ lúc nào trong vòng một năm tới (31.536.000 giây; giá trị được hỗ trợ tối đa), 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 đến máy chủ web của bạn. Thật tuyệt vời! Bạn đã ngay lập tức có được độ tin cậy và tốc độ nhờ tránh được mạng!

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

Xác thực lại máy chủ cho các URL không có phiên bản

Rất tiếc, không phải URL nào bạn tải cũng được tạo phiên bản. Có thể bạn không thể thêm bước xây 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 của thành phần. Ngoài ra, mọi ứng dụng web đều cần có tệp HTML. Những tệp đó (gần như!) sẽ không bao giờ chứa thông tin về phiên bản, vì 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 trường hợp mà bạn cần phải chấp nhận thất bại. Chỉ riêng việc lưu vào bộ nhớ đệm HTTP là chưa đủ mạnh để tránh hoàn toàn mạng. (Đừng lo, bạn sẽ sớm tìm hiểu về trình chạy dịch vụ, công cụ này sẽ hỗ trợ chúng ta lật ngược thế cờ.) Tuy nhiên, bạn có thể thực hiện một vài bước để đảm bảo các yêu cầu mạng diễn ra 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 lưu URL không có 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 trước khi sử dụng phiên bản đã lưu vào bộ nhớ đệm của URL.
  • no-store. Thao tác này 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. 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 lưu trữ trong bất kỳ bộ nhớ đệm nào.

Hãy xem Phụ lục: Sơ đồ quy trình Cache-Control để hình dung quy trình quyết định(các) giá trị Cache-Control cần sử dụng. Cache-Control cũng có thể chấp nhận danh sách các lệnh được phân tách bằng dấu phẩy. Xem Phụ lục: Ví dụ về Cache-Control.

Việc đặt ETag hoặc Last-Modified cũng có thể giúp ích. Như đã đề cập trong phần Tiêu đề phản hồi, ETag và Last-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. Bạn nên sử dụng ETag vì thuộc tính này chính xác hơn.

Ví dụ về ETag

Giả sử 120 giây đã trôi qua kể từ lần tìm nạp ban đầu và trình duyệt đã bắt đầu 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 thấy 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 này 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 xuống cùng một thông tin đã có trong bộ nhớ đệm!

Đó là vấn đề mà mã thông báo xác thực (như được chỉ định trong tiêu đề ETag) được thiết kế để giải quyết. Máy chủ tạo và trả về một mã thông báo tuỳ ý, thường là hàm băm hoặc một số dấu vân tay khác của nội dung tệp. Trình duyệt không cần biết cách tạo vân tay số; trình duyệt chỉ cần gửi vân tay số đế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.

Việc đặt ETag hoặc Last-Modified giúp yêu cầu xác thực lại hiệu quả hơn nhiều bằng cách cho phép yêu cầu đó kích hoạt tiêu đề của yêu cầu If-Modified-Since hoặc If-None-Match được đề cập trong phần Tiêu đề yêu cầu.

Khi một máy chủ web được định cấu hình đúng cách thấy các tiêu đề của yêu cầu đến đó, máy chủ này có thể xác nhận xem phiên bản 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, thì máy chủ có thể phản hồi bằng phản hồi HTTP 304 Not Modified, tương đương với "Này, 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, phản hồi này thường nhanh hơn nhiều so với việc phải thực sự gửi lại bản sao của tài nguyên thực tế được yêu cầu.

Hình ảnh minh hoạ một ứng dụng khách 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à thê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ị khớp nhau, vì vậy, 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ì nó giúp 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ợ trong tất cả trình duyệt và không mất quá nhiều công sức để thiết lập.

Bạn nên bắt đầu bằng các cấu hình Cache-Control sau:

  • 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 những tài nguyên không bao giờ được lưu vào bộ nhớ đệm.
  • Cache-Control: max-age=31536000 cho tài nguyên theo phiên bản.

Ngoài ra, 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 đã hết hạn trong bộ nhớ đệm 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 thêm ngoài 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 về lưu vào bộ nhớ đệm và những điều cần lưu ý về max-age của Jake Archibald.

Hãy xem bài viết Love your cache (Yêu thích bộ nhớ đệm) để biết hướng dẫn về cách tối ưu hoá việ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 để 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ệ rời bỏ. Nếu một phần của 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 việc tách mã cập nhật thường xuyên thành một tệp riêng biệt và sử dụng chiến lược lưu vào bộ nhớ đệm trong thời gian ngắn cho mã cập nhật thường xuyên và chiến lược lưu vào bộ nhớ đệm trong thời gian dài cho mã không thay đổi thường xuyên.
  • Hãy xem lệnh stale-while-revalidate mới nếu chính sách Cache-Control của bạn chấp nhận một mức độ lỗi thời nhất định.

Phụ lục: Sơ đồ quy trình Cache-Control

Sơ đồ quy trình
Quy trình quyết định để đặt tiêu đề Cache-Control.

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

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