ETag (Entity Tag) is an HTTP header that assigns a unique identifier to a resource version on the server, allowing the client to efficiently check the relevance of cached data. On a repeat request, the browser or application sends the saved ETag, and the server compares it with the current one: if they match, it returns a 304 Not Modified status without a response body. According to RFC 7232 (IETF, 2014), conditional requests with ETag reduce data transfer volume by up to 95% for frequently requested resources. This makes the header critically important for mobile application performance.
Key Takeaways
ETag (Entity Tag) is an HTTP response header containing a unique identifier for a specific version of a resource. The server computes the ETag based on the file contents, its metadata, or revision number and sends it to the client in the response to a GET request. The client saves this identifier and on subsequent requests to the same resource sends it in the If-None-Match header. If the resource has not changed, the server responds with 304 Not Modified, and the client uses its cached copy.
The ETag format is defined in RFC 7232 as a quoted string: "33a64df551425fcc55e4d42a148795d9f25f89d4". The value can be an SHA-1 hash of the file contents, an incremental version number, a combination of inode-number-time for static files, or an arbitrary token generated by the server. The only requirement is that the value must change whenever the resource changes and must not change if the resource remains the same.
ETag belongs to the conditional request mechanisms — one of the basic HTTP protocol optimisations. Unlike unconditional requests where the server always returns a full response, a conditional request allows the client to check cache relevance without reloading data. According to HTTP Archive (2025), about 40% of all HTTP responses are 304 Not Modified thanks to proper ETag and Last-Modified configuration.
ETag is used in REST APIs to optimize loading of data collections — if the object list has not changed, the client receives 304 without transferring the entire JSON. For static files (CSS, JS, images), ETag allows CDNs and browsers to efficiently check cache freshness. In mobile applications, ETag is critical for background synchronization: the app checks whether data on the server has changed and downloads updates only when necessary. This saves traffic and device battery.
The full ETag lifecycle consists of four steps. The server generates an ETag on the first request and returns it in the response header. The client saves the ETag together with the cached resource. On a repeat request, the client sends the If-None-Match header with the saved ETag value. The server compares the received value with the current resource ETag: if they match, it returns 304 Not Modified with an empty body; if they don’t match, it returns 200 OK with the new resource and new ETag.
// Client request with If-None-Match
GET /api/users HTTP/1.1
Host: example.com
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
// Server response — resource unchanged
HTTP/1.1 304 Not Modified
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
In a mobile application, this cycle can be implemented via an HTTP client with caching support. OkHttp, for example, automatically manages ETag through CacheInterceptor: it saves the response ETag and adds If-None-Match on repeat requests. When receiving 304, OkHttp returns the cached data. OkHttp supports ETag without additional configuration — just enable the cache via OkHttpClient.Builder.cache().
The server can compute ETags in different ways: via MD5 or SHA content hash, via a revision number from the database (e.g., updated_at from MySQL), via a combination of inode + mtime + size for static files (Nginx generates ETags exactly this way). For dynamic APIs, content hash is the most reliable: if the JSON response changes even one field, the ETag will change. However, computing a hash on every request puts load on the CPU — for high-load systems, it’s better to use an incremental version number.
RFC 7232 defines two types of ETags: strong and weak. A strong ETag means that two representations of the resource are identical byte-for-byte — not a single bit differs. A weak ETag (prefix W/) guarantees only semantic equivalence: the content may differ at the serialization level (whitespace, JSON field order), but the data is considered the same for the client. Weak ETags are marked with the W/ prefix, for example W/"1a2b3c".
The choice of ETag type depends on the comparison accuracy requirements. For static files (CSS, JS, images), strong ETags are preferable — if the file has changed, the client must get the new version. For dynamic APIs, where the same JSON might be serialized with different field order or formatting, weak ETags provide more flexibility: the server generates the ETag based on business data rather than the string representation.
| ETag Type | Format | Guarantee | Usage |
|---|---|---|---|
| Strong | "hash" | Byte-for-byte identity | Static files, binary resources |
| Weak | W/"hash" | Semantic equivalence | JSON API, dynamic pages |
A limitation of weak ETags: they cannot be used with range requests. If the client requests a part of a file, the server must return a strong ETag to guarantee that the fragment corresponds to the full resource. Weak ETags do not provide such a guarantee. In other scenarios, weak ETags are safe and recommended for APIs.
ETag and Last-Modified are two HTTP headers for conditional requests that are often used together. Last-Modified indicates the last modification date of a resource and works with the If-Modified-Since header. ETag provides a unique version identifier and works with If-None-Match. Each has its advantages and limitations, and combining them provides maximum caching efficiency.
Last-Modified is simpler to implement — the server automatically gets the date from the file system or updates the updated_at field in the database. However, the date has second-level precision, which is insufficient for resources that change multiple times per second. Additionally, Last-Modified does not distinguish between different states: if a file is overwritten with the same version, the date changes but the content doesn’t, so the client will reload identical data.
ETag is more precise: it changes only when the content actually changes. If the server restores a previous version from backup, the ETag changes. If a file is overwritten with the same data, the ETag remains the same, and the client does not reload. Combined usage is recommended by the HTTP specification: the server returns both headers, the client sends If-None-Match and If-Modified-Since simultaneously. If at least one header indicates a change, the server returns a new resource.
Per the specification, ETag has priority over Last-Modified. If the server receives If-None-Match, it should check only the ETag, ignoring If-Modified-Since. This prevents race conditions: if the resource changed between the client sending Last-Modified and the server checking it, ETag will be the fresher indicator. In practice, servers usually check both headers, but when results mismatch, ETag wins.
ETag configuration depends on the server type. Nginx generates ETags for static files automatically based on inode, mtime, and size. Apache uses the FileETag mechanism. For dynamic applications on Node.js, PHP, Python, Ruby, ETags need to be generated programmatically — via response hash, data version number, or a combination of request parameters.
func etagMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter,
r *http.Request) {
// Generate ETag based on data
etag := generateETag(r.URL.Path)
w.Header().Set("ETag", etag)
// Check If-None-Match
if r.Header.Get("If-None-Match") == etag {
w.WriteHeader(http.StatusNotModified)
return
}
next.ServeHTTP(w, r)
})
}
Middleware in Go intercepts the request, generates an ETag for the requested URL (for example, computes a data hash from cache or DB) and sets the response header. If the client sent If-None-Match and it matches the current ETag, the server returns 304 Not Modified immediately, without calling the main handler. In production, you should add caching of computed ETags by URL and parameters to reduce server load.
In a multi-server configuration (round-robin or anycast), the ETag must be the same on all nodes for the same resource. If the ETag is generated based on the file inode and the site is deployed on multiple servers, the values will differ. The solution is to use a content hash or centralized version storage (Redis, etcd). The second issue is gzip compression: Nginx changes the ETag when compression is enabled, which can cause redundant 304 responses. You need to configure gzip_vary on to synchronize the ETag with compressed content.
Frequently Asked Questions
Yes, if the server has not explicitly prevented it. An ETag does not have to be globally unique — it is unique within a specific URL. For static files, collisions are unlikely when using an SHA hash, but custom generators may produce duplicates.
ETag is most effective for resources that are requested repeatedly and rarely change: static assets, API lists, configurations. For unique pages that are loaded once (for example, an order confirmation page), ETag provides no benefit.
CDNs consider the ETag in origin requests to check cache freshness. If the ETag of a resource on the origin has changed, the CDN loads the new version. Cloudflare and Fastly support ETag as a standard cache invalidation mechanism at the origin level.
RFC 7232 does not limit the ETag length, but servers and proxies may truncate or ignore excessively long values. It is recommended to use a 20–40 character hash or a combination of version identifier and checksum.
These are not mutually exclusive mechanisms. Cache-Control defines the caching policy (how long to store, who is allowed), while ETag is a validation mechanism for cached resources. The optimal configuration includes both headers together.
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