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:

  • Các phản hồi lớn yêu cầu 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 ai đó truy cập vào 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 mạng không cần thiết đều là sự lãng phí tiền của họ.

Làm cách nào để bạn có thể tránh các yêu cầu không cần thiết về mạng? Bộ nhớ đệm HTTP của trình duyệt là tuyến phòng vệ đầ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 toàn bộ thời gian phản hồi được lưu vào bộ nhớ đệm, nhưng cách này có hiệu quả, được hỗ trợ trên tất cả cá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ề việc triển khai hiệu quả việc lưu vào bộ nhớ đệm HTTP.

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

Trên thực tế, không có một API duy nhất nào được gọi là Bộ nhớ đệm HTTP. Đây là tên chung của một tập hợp API trên nền tảng web. Những API đó được hỗ trợ trong tất cả các trình duyệt:

Cache-Control

Hỗ trợ trình duyệt

  • Đúng
  • 12
  • Đúng
  • Đúng

Nguồn

ETag

Hỗ trợ trình duyệt

  • Đúng
  • 12
  • Đúng
  • Đúng

Nguồn

Last-Modified

Hỗ trợ trình duyệt

  • Đúng
  • 12
  • Đúng
  • Đúng

Nguồn

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

Trước tiên, tất cả yêu cầu HTTP mà trình duyệt thực hiện đượ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 khớp, phản hồi sẽ được đọc từ bộ nhớ đệm. Điều này giúp loại bỏ cả độ trễ mạng và chi phí dữ liệu phát sinh.

Hành vi của Bộ nhớ đệm HTTP được kiểm soát bằng tổ hợp tiêu đề yêu cầutiê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ã dành cho ứng dụng web của mình (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).

Tham khảo bài viết về Lưu vào bộ nhớ đệm HTTP của MDN để biết tổng quan khái niệm chuyên sâu hơn.

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

Có một số tiêu đề quan trọng cần được bao gồm trong 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 đề này thay cho bạn khi đưa ra yêu cầu. Những tiêu đề yêu cầu có ảnh hưởng đến việc kiểm tra độ mới, chẳng hạn như If-None-MatchIf-Modified-Since sẽ 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 vui – điều đó 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 cần làm gì thêm.

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 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à khoảng thời gian mà trình duyệt và các bộ nhớ đệm trung gian khác sẽ lưu từng phản hồi vào bộ nhớ đệm.
  • ETag. Khi tìm thấy một phản hồi đã hết hạn được lưu vào bộ nhớ đệm, trình duyệt có thể gửi một mã thông báo nhỏ (thường là một hàm băm của nội dung tệp) tới 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 phải tải xuống 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 đã 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 để đặt các tiêu đề đó theo mặc định, trong khi một số máy chủ khác bỏ hoàn toàn tiêu đề trừ phi bạn định cấu hình chúng một cách rõ ràng. Các chi tiết cụ thể về cách định cấu hình tiêu đề thay đổi đáng kể tuỳ thuộc vào máy chủ web bạn sử dụng và bạn nên tham khảo tài liệu của máy chủ đó để biết thông tin chi tiết chính xác nhất.

Để tiết kiệm thời gian tìm kiếm, dưới đây là hướng dẫn 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 vô hiệu hoá chức năng lưu HTTP vào bộ nhớ đệm! Thay vào đó, các trình duyệt sẽ dự đoán một cách hiệu quả loại hành vi lưu vào bộ nhớ đệm nào 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 kiểm soát nhiều hơn so với những đề xuất đó, vì vậy hãy dành thời gian để định cấu hình 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 trong thời gian dài cho các URL có phiên bản mới

Cách URL được tạo phiên bản có thể hỗ trợ chiến lược lưu vào bộ nhớ đệm của bạn
Bạn nên sử dụng URL được tạo phiên bản vì chúng giúp bạn dễ dàng vô hiệu hoá các phản hồi đã 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 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 các trình duyệt về việc cập nhật bản sao "cũ" của tệp được lưu vào bộ nhớ đệm? Ít nhất là bạn không thể 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 phiên bản đó không còn mới (như được max-age hoặc expires xác định) hay cho đến khi bị xoá khỏi bộ nhớ đệm vì một số lý do khác, ví dụ: 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ử dụng phiên bản mới, trong khi người dùng đã lưu vào bộ nhớ đệm một bản sao trướ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 để tận dụng tối đa 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 thực hiện việc này bằng cách nhúng một 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 yêu cầu về URL chứa "vân tay" hoặc thông tin phiên bản và có nội dung không bao giờ thay đổi, hãy thêm Cache-Control: max-age=31536000 vào câu trả lờ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 kỳ lúc nào trong vòng một năm tiếp theo (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 thực hiện yêu cầu mạng đến máy chủ web của bạn. Thật tuyệt! Bạn ngay lập tức nhận được độ tin cậy và tốc độ nhờ tránh mạng!

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

Xác thực lại máy chủ cho các 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 thêm được bước 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 thành phần. Và mọi ứng dụng web đều cần có tệp HTML – các tệp đó (hầu như!) sẽ không bao giờ chứa thông tin phiên bản, vì sẽ không ai bận tâm 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 phải chấp nhận thất bại. Nếu chỉ lưu vào bộ nhớ đệm HTTP thì không đủ mạnh để tránh hoàn toàn mạng. (Đừng lo—bạn sẽ sớm tìm hiểu về service worker, nó sẽ cung cấp sự hỗ trợ mà chúng tôi cần để đưa trận chiến quay lại có lợi cho bạn.) Tuy nhiên, có một vài bước bạn có thể thực hiện để đảm bảo rằng các yêu cầu mạng nhanh nhất 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 tạo phiên bản vào bộ nhớ đệm:

  • no-cache. Lệnh này chỉ dẫn trình duyệt phải xác thực lại với máy chủ mỗi lần 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. 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 này có thể được lưu trữ bởi bất kỳ bộ nhớ đệm nào.

Xem Phụ lục: Sơ đồ quy trình Cache-Control để trực quan hoá 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: Cache-Control ví dụ.

Việc đặt ETag hoặc Last-Modified cũng có thể giúp ích. Như đã đề cập trong 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 được lưu vào bộ nhớ đệm đã hết hạn xuống hay không. Bạn nên sử dụng ETag vì lựa chọn này sẽ chính xác hơn.

Ví dụ về thẻ ETag

Giả sử rằng 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. Đầu tiên, trình duyệt 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 câu trả lời trước đó vì câu trả lờ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 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ư đã 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à một hàm băm hoặc một tệp tham chiếu 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ố mà chỉ cần gửi đến máy chủ trong yêu cầu tiếp theo. Nếu vân tay 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 kích hoạt các tiêu đề của yêu cầu If-Modified-Since hoặc If-None-Match được đề cập trong Tiêu đề của yêu cầu.

Khi 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ủ 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 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 câu lệnh "Xin chào, hãy tiếp tục sử dụng những tính năng bạn đã có!" Có rất ít dữ liệu để 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.

Hình ảnh máy 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 nên máy chủ 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 HTTP giảm các yêu cầu không cần thiết về mạng. Tính năng này được hỗ trợ trên tất cả các trình duyệt và bạn không cần 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 để biết các tài nguyên được tạo 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 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 thông tin cơ bản về việc sử dụng tiêu đề Cache-Control, hãy tham khảo hướng dẫn của Jake Archibald về Các phương pháp hay nhất để lưu vào bộ nhớ đệm và phương pháp tối đa hoá độ tuổi.

Tham khảo bài viết Yêu thích bộ nhớ đệm để tham khảo 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 những cách khác dưới đâ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 các URL khác nhau, 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 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 xem xét việc chia đoạn mã thường xuyên cập nhật 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 thời gian lưu vào bộ nhớ đệm dài cho 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 một mức độ lỗi thời nào đó.

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

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

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 lưu vào bộ nhớ đệm bởi trình duyệt và bộ nhớ đệm trung gian 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 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 Mọi bộ nhớ đệm đều có thể lưu trữ phản hồi này trong 1 năm.
no-store Không được phép lưu phản hồi này vào bộ nhớ đệm và phải tìm nạp đầy đủ phản hồi này cho mọi yêu cầu.