ETag là một header phản hồi HTTP chứa mã định danh duy nhất của phiên bản tài nguyên. Máy chủ tạo ETag dưới dạng hàm băm nội dung hoặc số phiên bản và trả về cho máy khách cùng với dữ liệu. Trong các yêu cầu tiếp theo, máy khách gửi mã định danh này trong header If-None-Match, cho phép máy chủ kiểm tra xem tài nguyên đã thay đổi hay chưa. Theo MDN Web Docs, 2025, ETag là nền tảng của cơ chế yêu cầu GET có điều kiện trong HTTP. 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 trong quá trình đồng bộ hóa ứng dụng di động lên tới 90%.
Các điểm chính
ETag (Entity Tag) là một header HTTP thuộc họ header có điều kiện dùng để xác thực tài nguyên được lưu trong bộ nhớ đệm. Máy chủ tính toán ETag dưới dạng hàm băm (MD5, SHA-256) hoặc số phiên bản tài nguyên và trả về trong phản hồi cho yêu cầu GET. Máy khách lưu trữ ETag cùng với dữ liệu và gửi nó trong header If-None-Match ở các yêu cầu tiếp theo. Nếu nội dung tài nguyên không thay đổi, máy chủ phản hồi với trạng thái 304 Not Modified mà không có nội dung phản hồi.
Đối với ứng dụng di động, ETag rất quan trọng vì nó giảm lượng dữ liệu được tải xuống. Mỗi lần khởi chạy hoặc đồng bộ hóa, ứng dụng kiểm tra tính mới của tài nguyên bằng yêu cầu If-None-Match — thay vì tải toàn bộ dữ liệu, nó nhận được 304 và sử dụng bản sao cục bộ. Theo Google Chrome Team (2024), việc sử dụng ETag trong API di động giảm kích thước phản hồi trung bình 87% cho danh sách và 94% cho các đối tượng riêng lẻ.
ETag được tạo ở phía máy chủ và có thể tất định (giống hệt nhau cho nội dung giống hệt, hữu ích cho bộ nhớ đệm dùng chung) hoặc duy nhất cho mỗi phản hồi (để xác thực nghiêm ngặt). Trong REST API được thiết kế cho đồng bộ hóa di động, sự kết hợp phổ biến nhất là hàm băm nội dung và số phiên bản bản ghi trong cơ sở dữ liệu.
ETag mạnh (strong ETag) là các định danh thay đổi theo bất kỳ sửa đổi nội dung nào, bao gồm cả những thay đổi nhỏ (khoảng trắng, định dạng). Định dạng: “abc123def” (trong dấu ngoặc kép, không có tiền tố). ETag mạnh đảm bảo rằng tài nguyên không thay đổi từng byte. Chúng bắt buộc phải có cho yêu cầu phạm vi (Range requests) và để kiểm tra tính toàn vẹn của các bản tải xuống một phần.
ETag yếu (weak ETag) là các định danh có tiền tố W/, ví dụ W/“abc123def”. Chúng cho phép tài nguyên tương đương về mặt ngữ nghĩa ngay cả khi biểu diễn byte khác nhau. ETag yếu hữu ích cho các máy chủ tạo phản hồi động với khoảng trắng hoặc định dạng khác nhau nhưng cùng ý nghĩa. Tuy nhiên, ETag yếu không hỗ trợ yêu cầu phạm vi.
So sánh các loại ETag:
| Đặc điểm | ETag mạnh | ETag yếu |
|---|---|---|
| Định dạng | “hash” | W/“hash” |
| Độ nhạy | Từng byte | Ngữ nghĩa |
| Yêu cầu Range | Được hỗ trợ | Không được hỗ trợ |
| Bộ nhớ đệm CDN | Lý tưởng | Hạn chế |
| Đồng bộ hóa | Độ chính xác cao | Cho phép xung đột |
Last-Modified là một header HTTP cho biết ngày và giờ sửa đổi lần cuối của tài nguyên. Máy khách gửi lại nó trong header If-Modified-Since. Last-Modified đơn giản hơn để triển khai (máy chủ chỉ cần một ngày), nhưng có những hạn chế cơ bản: độ phân giải một giây (hai thay đổi trong cùng một giây không thể phân biệt) và không thể xác định liệu nội dung có thay đổi hay không nếu dấu thời gian giống nhau (ví dụ: sau khi khôi phục từ bản sao lưu).
ETag giải quyết những vấn đề này: hàm băm nội dung thay đổi theo mọi sửa đổi bất kể thời gian. Do đó, các REST API hiện đại sử dụng kết hợp cả hai header: ETag để xác thực chính xác và Last-Modified để lọc gần đúng trên CDN. Apache HTTP Server và Nginx tạo cả hai header cho các tệp tĩnh theo mặc định.
Đối với ứng dụng di động có đồng bộ hóa, ETag quan trọng hơn vì nó cho phép phát hiện xung đột chỉnh sửa. Nếu máy khách gửi yêu cầu PUT với If-Match: “etag”, máy chủ từ chối yêu cầu nếu tài nguyên đã bị sửa đổi bởi máy khách khác (khóa lạc quan). Last-Modified không thể đảm bảo độ tin cậy đó do độ chính xác ở cấp giây.
Hãy xem xét triển khai phía máy khách của ETag trong ứng dụng di động sử dụng Kotlin với Retrofit và OkHttp. Trong mỗi yêu cầu GET, máy khách lưu ETag từ phản hồi và trong yêu cầu tiếp theo gửi nó trong header If-None-Match. Nếu máy chủ trả về 304, dữ liệu không được tải lại.
Thiết lập máy khách OkHttp với bộ nhớ đệm ETag:
class EtagClient {
private val etagCache =
mutableMapOf<String, String>()
private val client = OkHttpClient.Builder().build()
suspend fun fetchWithEtag(
url: String
): Result<String> {
val request = Request.Builder()
.url(url)
.header("If-None-Match",
etagCache[url] ?: "")
.build()
val response = client.newCall(request).await()
return when (response.code) {
304 -> Result.success(
"not_modified")
200 -> {
response.header("ETag")?.let {
etagCache[url] = it
}
Result.success(response.body?.string()
?: "")
}
else -> Result.failure(
Exception("HTTP ${response.code}"))
}
}
}
Máy khách lưu ETag sau phản hồi 200 thành công và gửi nó trong header If-None-Match trong yêu cầu tiếp theo. Khi nhận được phản hồi 304, máy khách biết rằng phiên bản cục bộ là mới nhất và không lãng phí băng thông để tải lại. Mô hình này giảm chi phí mạng của ứng dụng di động từ 80–90% cho các tài nguyên được yêu cầu thường xuyên.
ETag là một cơ chế chính để tối ưu hóa đồng bộ hóa ứng dụng di động với REST API. Trong sơ đồ đồng bộ tiêu chuẩn, máy khách đầu tiên yêu cầu danh sách tài nguyên với xác thực ETag — nếu không có tài nguyên nào thay đổi, máy chủ trả về 304 và máy khách hoàn tất đồng bộ hóa. Nếu có thay đổi, máy chủ chỉ trả về các tài nguyên đã sửa đổi. Cách tiếp cận này được gọi là đồng bộ hóa delta và rất quan trọng đối với các thiết bị di động có băng thông hạn chế.
Trong các kịch bản khóa lạc quan, ETag được sử dụng để ngăn xung đột Lost Update. Khi máy khách gửi yêu cầu PUT để cập nhật tài nguyên, nó bao gồm header If-Match: “etag”. Nếu ETag không khớp (máy khách khác đã sửa đổi tài nguyên), máy chủ phản hồi với 412 Precondition Failed và máy khách phải tải lại phiên bản hiện tại và thử lại sửa đổi. Cách tiếp cận này đảm bảo tính nhất quán dữ liệu mà không cần khóa ở cấp cơ sở dữ liệu.
Đối với các hệ thống phân tán có chế độ ngoại tuyến, ETag được sử dụng kết hợp với Giải quyết xung đột. Máy khách đồng bộ hóa bằng cách lấy ETag hiện tại cho tất cả tài nguyên. Khi gửi thay đổi, máy chủ kiểm tra If-Match — nếu ETag không khớp, một xung đột được ghi lại và giải quyết theo chiến lược đã chọn (LWW, Merge). Theo Postman API Report (2025), 67% REST API sản xuất cho ứng dụng di động sử dụng ETag làm cơ chế xác thực phiên bản chính.
Câu hỏi thường gặp
ETag là một header phản hồi HTTP chứa mã định danh duy nhất của phiên bản tài nguyên. Máy khách sử dụng nó cho các yêu cầu có điều kiện: nếu tài nguyên không thay đổi, máy chủ trả về 304 Not Modified mà không có nội dung phản hồi, tiết kiệm băng thông.
ETag sử dụng hàm băm nội dung để so sánh chính xác. Last-Modified dựa trên ngày sửa đổi với độ chính xác đến giây. ETag đáng tin cậy hơn để phát hiện các thay đổi thực tế và hỗ trợ khóa lạc quan qua If-Match.
ETag mạnh (không có tiền tố) phân biệt tài nguyên từng byte. ETag yếu (có tiền tố W/) cho phép tương đương ngữ nghĩa. ETag mạnh cần thiết cho yêu cầu phạm vi, ETag yếu cho nội dung được tạo động.
ETag giảm băng thông 80–90%: máy khách kiểm tra tính mới của tất cả tài nguyên qua If-None-Match, chỉ tải xuống những tài nguyên đã thay đổi. Nếu không có ETag, máy khách sẽ tải toàn bộ dữ liệu trong mỗi lần đồng bộ, lãng phí băng thông và pin.
Máy chủ tính toán ETag dưới dạng hàm băm (MD5, SHA-256) của nội dung phản hồi hoặc sử dụng số phiên bản bản ghi từ cơ sở dữ liệu. Trong Spring Boot, chú thích @Cacheable với etag = true là đủ. Trong Express.js, middleware etag được bật theo mặc định.
Tổng kế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