ETag is an HTTP response header that contains a unique identifier for a resource version. The server generates an ETag as a content hash or version number and returns it to the client along with the data. On subsequent requests, the client sends this identifier in the If-None-Match header, allowing the server to check whether the resource has changed. According to MDN Web Docs, 2025, ETag is the foundation of the conditional GET request mechanism in HTTP. Conditional requests with ETag reduce the amount of data transferred during mobile app synchronization by up to 90%.
Key Takeaways
ETag (Entity Tag) is an HTTP header from the conditional header family that validates cached resources. The server computes an ETag as a hash (MD5, SHA-256) or a resource version number and returns it in response to a GET request. The client stores the ETag along with the data and sends it in the If-None-Match header on subsequent requests. If the resource content has not changed, the server responds with a 304 Not Modified status without a response body.
For mobile applications, ETag is critically important because it reduces the amount of data downloaded. On each launch or synchronization, the app checks resource freshness with an If-None-Match request — instead of fully loading data, it receives a 304 and uses the local copy. According to Google Chrome Team (2024), using ETag in mobile APIs reduces the average response size by 87% for lists and 94% for individual objects.
ETag is generated server-side and can be either deterministic (identical for identical content, useful for shared caches) or unique per response (for strict validation). In REST APIs designed for mobile synchronization, the most common combination is a content hash and a database record version number.
Strong ETags are identifiers that change with any content modification, including insignificant ones (whitespace, formatting). Format: “abc123def” (in double quotes, no prefix). Strong ETags guarantee that the resource has not changed byte-by-byte. They are required for Range requests and for verifying the integrity of partial downloads.
Weak ETags are identifiers with the W/ prefix, for example W/“abc123def”. They allow the resource to be semantically equivalent even if the byte representation differs. Weak ETags are useful for servers that dynamically generate responses with different whitespace or formatting but the same meaning. However, weak ETags do not support Range requests.
Comparison of ETag types:
| Characteristic | Strong ETag | Weak ETag |
|---|---|---|
| Format | “hash” | W/“hash” |
| Sensitivity | Byte-by-byte | Semantic |
| Range requests | Supported | Not supported |
| CDN caching | Ideal | Limited |
| Synchronization | High precision | Allows collisions |
Last-Modified is an HTTP header indicating the date and time of the last resource modification. The client sends it back in the If-Modified-Since header. Last-Modified is simpler to implement (the server only needs a date), but has fundamental limitations: one-second resolution (two changes within the same second are indistinguishable) and the inability to determine whether content has changed if the timestamp is the same (e.g., after a backup restore).
ETag solves these problems: the content hash changes with any modification regardless of time. Therefore, modern REST APIs use a combination of both headers: ETag for precise validation and Last-Modified for approximate CDN filtering. Apache HTTP Server and Nginx generate both headers for static files by default.
For mobile applications with synchronization, ETag is more critical because it allows detecting editing conflicts. If a client sends a PUT request with If-Match: “etag”, the server rejects the request if the resource was modified by another client (optimistic locking). Last-Modified cannot guarantee such reliability due to second-level precision.
Let’s look at a client-side implementation of ETag in a mobile app using Kotlin with Retrofit and OkHttp. On each GET request, the client saves the ETag from the response, and on the next request sends it in the If-None-Match header. If the server returns 304, the data is not re-downloaded.
Setting up an OkHttp client with ETag caching:
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}"))
}
}
}
The client saves the ETag after a successful 200 response and sends it in the If-None-Match header on the next request. On a 304 response, the client knows the local version is current and does not waste traffic on re-downloading. This pattern reduces mobile app network costs by 80–90% for frequently requested resources.
ETag is a key mechanism for optimizing mobile app synchronization with REST APIs. In a standard sync scheme, the client first requests a list of resources with ETag validation — if no resource has changed, the server returns 304 and the client completes synchronization. If changes exist, the server returns only the modified resources. This approach is called delta synchronization and is critically important for mobile devices with limited traffic.
In optimistic locking scenarios, ETag is used to prevent Lost Update conflicts. When a client sends a PUT request to update a resource, it includes the If-Match: “etag” header. If the ETag does not match (another client has already modified the resource), the server responds with 412 Precondition Failed, and the client must re-fetch the current version and retry the modification. This approach ensures data consistency without database-level locks.
For distributed systems with offline mode, ETag is used in combination with Conflict Resolution. The client synchronizes by fetching current ETags for all resources. When sending changes, the server checks If-Match — if the ETag does not match, a conflict is registered and resolved according to the chosen strategy (LWW, Merge). According to the Postman API Report (2025), 67% of production REST APIs for mobile applications use ETag as the primary version validation mechanism.
Frequently Asked Questions
ETag is an HTTP response header containing a unique resource version identifier. The client uses it for conditional requests: if the resource has not changed, the server returns 304 Not Modified without a response body, saving traffic.
ETag uses a content hash for precise comparison. Last-Modified is based on the modification date with second-level precision. ETag is more reliable for detecting actual changes and supports optimistic locking via If-Match.
Strong ETags (no prefix) distinguish resources byte-by-byte. Weak ETags (with W/ prefix) allow semantic equivalence. Strong ETags are required for Range requests, weak ones are for dynamically generated content.
ETag reduces traffic by 80–90%: the client checks the freshness of all resources via If-None-Match, downloading only the changed ones. Without ETag, the client would download full data on each synchronization, wasting traffic and battery.
The server computes an ETag as a hash (MD5, SHA-256) of the response content or uses a database record version number. In Spring Boot, the @Cacheable annotation with etag = true is sufficient. In Express.js, the etag middleware is enabled by default.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also