ETag (Entity Tag) là một tiêu đề HTTP gán một định danh duy nhất cho một phiên bản tài nguyên trên máy chủ, cho phép máy khách kiểm tra hiệu quả tính phù hợp của dữ liệu được lưu trong bộ nhớ đệm. Khi yêu cầu lặp lại, trình duyệt hoặc ứng dụng gửi ETag đã lưu và máy chủ so sánh nó với hiện tại: nếu khớp, nó trả về trạng thái 304 Not Modified mà không có nội dung phản hồi. Theo RFC 7232 (IETF, 2014), các yêu cầu có điều kiện với ETag giảm khối lượng dữ liệu truyền tải lên tới 95% cho các tài nguyên được yêu cầu thường xuyên. Đ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 di động.
Điểm chính
ETag (Entity Tag) là một tiêu đề phản hồi HTTP chứa định danh duy nhất cho một phiên bản cụ thể của một tài nguyên. Máy chủ tính toán ETag dựa trên nội dung tệp, siêu dữ liệu hoặc số lần sửa đổi và gửi nó đến máy khách trong phản hồi cho một yêu cầu GET. Máy khách lưu định danh này và trong các yêu cầu tiếp theo đến cùng tài nguyên, gửi nó trong tiêu đề If-None-Match. Nếu tài nguyên không thay đổi, máy chủ phản hồi với 304 Not Modified và máy khách sử dụng bản sao đã lưu trong bộ nhớ đệm.
Định dạng ETag được xác định trong RFC 7232 dưới dạng chuỗi được đặt trong dấu ngoặc kép: "33a64df551425fcc55e4d42a148795d9f25f89d4". Giá trị có thể là hash SHA-1 của nội dung tệp, số phiên bản tăng dần, sự kết hợp inode-số-thời gian cho các tệp tĩnh hoặc một token ngẫu nhiên do máy chủ tạo. Yêu cầu duy nhất là giá trị phải thay đổi khi tài nguyên thay đổi và không được thay đổi nếu tài nguyên giữ nguyên.
ETag thuộc về cơ chế yêu cầu có điều kiện (conditional requests) — một trong những tối ưu cơ bản của giao thức HTTP. Không giống như các yêu cầu vô điều kiện, nơi máy chủ luôn trả về phản hồi đầy đủ, một yêu cầu có điều kiện cho phép máy khách kiểm tra tính phù hợp của bộ nhớ đệm mà không cần tải lại dữ liệu. Theo HTTP Archive (2025), khoảng 40% tất cả các phản hồi HTTP là 304 Not Modified nhờ cấu hình ETag và Last-Modified phù hợp.
ETag được sử dụng trong REST API để tối ưu hóa việc tải các bộ sưu tập dữ liệu — nếu danh sách đối tượng không thay đổi, máy khách nhận được 304 mà không cần truyền toàn bộ JSON. Đối với các tệp tĩnh (CSS, JS, hình ảnh), ETag cho phép CDN và trình duyệt kiểm tra hiệu quả tính mới của bộ nhớ đệm. Trong các ứng dụng di động, ETag rất quan trọng cho đồng bộ nền: ứng dụng kiểm tra xem dữ liệu trên máy chủ đã thay đổi chưa và tải xuống các bản cập nhật chỉ khi cần thiết. Điều này tiết kiệm băng thông và pin thiết bị.
Vòng đời đầy đủ của ETag bao gồm bốn bước. Máy chủ tạo ETag ở yêu cầu đầu tiên và trả về nó trong tiêu đề phản hồi. Máy khách lưu ETag cùng với tài nguyên đã lưu trong bộ nhớ đệm. Khi yêu cầu lặp lại, máy khách gửi tiêu đề If-None-Match với giá trị ETag đã lưu. Máy chủ so sánh giá trị nhận được với ETag hiện tại của tài nguyên: nếu khớp, nó trả về 304 Not Modified với nội dung rỗng; nếu không khớp, nó trả về 200 OK với tài nguyên mới và ETag mới.
// Yêu cầu máy khách với If-None-Match
GET /api/users HTTP/1.1
Host: example.com
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
// Phản hồi máy chủ — tài nguyên không thay đổi
HTTP/1.1 304 Not Modified
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Trong một ứng dụng di động, chu trình này có thể được triển khai thông qua một trình khách HTTP hỗ trợ bộ nhớ đệm. Ví dụ, OkHttp tự động quản lý ETag thông qua CacheInterceptor: nó lưu ETag phản hồi và thêm If-None-Match trong các yêu cầu lặp lại. Khi nhận được 304, OkHttp trả về dữ liệu đã lưu trong bộ nhớ đệm. OkHttp hỗ trợ ETag mà không cần cấu hình thêm — chỉ cần bật bộ nhớ đệm qua OkHttpClient.Builder.cache().
Máy chủ có thể tính ETags theo nhiều cách khác nhau: thông qua hash MD5 hoặc SHA của nội dung, thông qua số lần sửa đổi từ cơ sở dữ liệu (ví dụ: updated_at từ MySQL), thông qua sự kết hợp inode + mtime + kích thước cho các tệp tĩnh (Nginx tạo ETags theo cách này). Đối với API động, hash nội dung là đáng tin cậy nhất: nếu phản hồi JSON thay đổi dù chỉ một trường, ETag sẽ thay đổi. Tuy nhiên, tính toán hash cho mỗi yêu cầu gây tải cho CPU — đối với các hệ thống có tải cao, tốt hơn nên sử dụng số phiên bản tăng dần.
RFC 7232 định nghĩa hai loại ETags: mạnh (strong) và yếu (weak). ETag mạnh có nghĩa là hai biểu diễn của tài nguyên giống hệt nhau từng byte — không một bit nào khác biệt. ETag yếu (tiền tố W/) chỉ đảm bảo sự tương đương về mặt ngữ nghĩa: nội dung có thể khác ở mức tuần tự hóa (khoảng trắng, thứ tự trường JSON), nhưng dữ liệu được coi là giống nhau đối với máy khách. ETag yếu được đánh dấu bằng tiền tố W/, ví dụ W/"1a2b3c".
Việc lựa chọn loại ETag phụ thuộc vào yêu cầu về độ chính xác so sánh. Đối với các tệp tĩnh (CSS, JS, hình ảnh), ETag mạnh được ưu tiên hơn — nếu tệp đã thay đổi, máy khách phải nhận được phiên bản mới. Đối với API động, nơi cùng một JSON có thể được tuần tự hóa với thứ tự trường hoặc định dạng khác nhau, ETag yếu cung cấp tính linh hoạt hơn: máy chủ tạo ETag dựa trên dữ liệu kinh doanh thay vì biểu diễn chuỗi.
| Loại ETag | Định dạng | Bảo đảm | Ứng dụng |
|---|---|---|---|
| Strong (mạnh) | "hash" | Giống nhau từng byte | Tệp tĩnh, tài nguyên nhị phân |
| Weak (yếu) | W/"hash" | Tương đương ngữ nghĩa | API JSON, trang động |
Một hạn chế của ETag yếu: chúng không thể được sử dụng với yêu cầu phạm vi (Range requests). Nếu máy khách yêu cầu một phần của tệp, máy chủ phải trả về ETag mạnh để đảm bảo rằng đoạn đó tương ứng với tài nguyên đầy đủ. ETag yếu không cung cấp sự đảm bảo đó. Trong các tình huống khác, ETag yếu an toàn và được khuyên dùng cho các API.
ETag và Last-Modified là hai tiêu đề HTTP cho các yêu cầu có điều kiện thường được sử dụng cùng nhau. Last-Modified cho biết ngày sửa đổi lần cuối của một tài nguyên và hoạt động với tiêu đề If-Modified-Since. ETag cung cấp một định danh phiên bản duy nhất và hoạt động với If-None-Match. Mỗi loại có ưu điểm và hạn chế riêng, và việc kết hợp chúng mang lại hiệu quả bộ nhớ đệm tối đa.
Last-Modified đơn giản hơn để triển khai — máy chủ tự động lấy ngày từ hệ thống tệp hoặc cập nhật trường updated_at trong cơ sở dữ liệu. Tuy nhiên, ngày có độ chính xác ở mức giây, không đủ cho các tài nguyên thay đổi nhiều lần mỗi giây. Ngoài ra, Last-Modified không phân biệt các trạng thái khác nhau: nếu một tệp bị ghi đèn với cùng phiên bản, ngày thay đổi nhưng nội dung không thay đổi, do đó máy khách sẽ tải lại dữ liệu giống hệt.
ETag chính xác hơn: nó chỉ thay đổi khi nội dung thực sự thay đổi. Nếu máy chủ khôi phục phiên bản trước đó từ bản sao lưu, ETag thay đổi. Nếu một tệp bị ghi đèn với cùng dữ liệu, ETag giữ nguyên và máy khách không tải lại. Sử dụng kết hợp được khuyên bởi đặc tả HTTP: máy chủ trả về cả hai tiêu đề, máy khách gửi If-None-Match và If-Modified-Since đồng thời. Nếu ít nhất một tiêu đề cho thấy sự thay đổi, máy chủ trả về tài nguyên mới.
Theo đặc tả, ETag có quyền ưu tiên hơn Last-Modified. Nếu máy chủ nhận được If-None-Match, nó chỉ nên kiểm tra ETag, bỏ qua If-Modified-Since. Điều này ngăn chặn các điều kiện cạnh tranh: nếu tài nguyên thay đổi giữa lúc máy khách gửi Last-Modified và kiểm tra trên máy chủ, ETag sẽ là chỉ báo mới hơn. Trong thực tế, các máy chủ thường kiểm tra cả hai tiêu đề, nhưng khi kết quả không khớp, ETag thắng thế.
Cấu hình ETag phụ thuộc vào loại máy chủ. Nginx tự động tạo ETags cho các tệp tĩnh dựa trên inode, mtime và kích thước. Apache sử dụng cơ chế FileETag. Đối với các ứng dụng động trên Node.js, PHP, Python, Ruby, ETags cần được tạo theo chương trình — thông qua hash phản hồi, số phiên bản dữ liệu hoặc kết hợp các tham số yêu cầu.
func etagMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter,
r *http.Request) {
// Tạo ETag dựa trên dữ liệu
etag := generateETag(r.URL.Path)
w.Header().Set("ETag", etag)
// Kiểm tra If-None-Match
if r.Header.Get("If-None-Match") == etag {
w.WriteHeader(http.StatusNotModified)
return
}
next.ServeHTTP(w, r)
})
}
Middleware trong Go chặn yêu cầu, tạo ETag cho URL được yêu cầu (ví dụ, tính toán hash dữ liệu từ bộ nhớ đệm hoặc DB) và đặt tiêu đề phản hồi. Nếu máy khách đã gửi If-None-Match và nó khớp với ETag hiện tại, máy chủ trả về ngay 304 Not Modified mà không gọi trình xử lý chính. Trong môi trường sản xuất, nên thêm bộ nhớ đệm cho các ETag đã tính theo URL và tham số để giảm tải cho máy chủ.
Trong cấu hình nhiều máy chủ (round-robin hoặc anycast), ETag phải giống nhau trên tất cả các nút cho cùng một tài nguyên. Nếu ETag được tạo dựa trên inode của tệp và trang web được triển khai trên nhiều máy chủ, các giá trị sẽ khác nhau. Giải pháp là sử dụng hash nội dung hoặc kho lưu trữ phiên bản tập trung (Redis, etcd). Vấn đề thứ hai là nén gzip: Nginx thay đổi ETag khi bật nén, có thể gây ra các phản hồi 304 dư thừa. Cần cấu hình gzip_vary on để đồng bộ ETag với nội dung đã nén.
Câu hỏi thường gặp
Có, nếu máy chủ không ngăn chặn điều này một cách rõ ràng. ETag không cần phải duy nhất trên toàn cầu — nó duy nhất trong một URL cụ thể. Đối với các tệp tĩnh, xung đột khó xảy ra khi sử dụng hash SHA, nhưng các trình tạo tùy chỉnh có thể tạo ra các bản sao trùng lặp.
ETag hiệu quả nhất cho các tài nguyên được yêu cầu nhiều lần và ít khi thay đổi: tài sản tĩnh, danh sách API, cấu hình. Đối với các trang duy nhất được tải một lần (ví dụ: trang xác nhận đơn hàng), ETag không mang lại lợi ích.
CDN xem xét ETag trong các yêu cầu nguồn để kiểm tra tính mới của bộ nhớ đệm. Nếu ETag của một tài nguyên trên nguồn thay đổi, CDN tải phiên bản mới. Cloudflare và Fastly hỗ trợ ETag như một cơ chế vô hiệu hóa bộ nhớ đệm tiêu chuẩn ở cấp độ nguồn.
RFC 7232 không giới hạn độ dài ETag, nhưng các máy chủ và proxy có thể cắt bớt hoặc bỏ qua các giá trị quá dài. Nên sử dụng hash 20–40 ký tự hoặc kết hợp định danh phiên bản và tổng kiểm tra.
Đây không phải là các cơ chế loại trừ lẫn nhau. Cache-Control xác định chính sách bộ nhớ đệm (lưu trong bao lâu, ai được phép), trong khi ETag là cơ chế xác thực cho các tài nguyên đã lưu trong bộ nhớ đệm. Cấu hình tối ưu bao gồm cả hai tiêu đề cùng nhau.
Tóm tắt
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.
Đọc thêm