---
title: "What Is an E-Tag and Why Does Caching Need It?"
description: "What is e tag? Learn how ETags, If-None-Match, 304 responses, cache TTLs, and CDN validation work—and how to test them without serving stale content again."
url: https://www.zerobuffer.io/blogs/what-is-e-tag
date_published: 2026-06-22
date_modified: 2026-06-22
topic: "Caching, TTL & Cache Control (`C6`)"
keywords: ["what is e tag","what is cache ttl","HTTP ETag","ETag example","how to generate ETag","ETag format","`If-None-Match`","`304 Not Modified`","strong vs weak ETag"]
word_count: 2093
author: "Sahil Asopa"
author_url: https://www.zerobuffer.io/authors/sahil-asopa
author_profiles: ["https://www.linkedin.com/in/sahilasopa/","https://github.com/sahilasopa"]
publisher: ZeroBuffer
license: © 2026 Apexnova Private Limited
---
![Origin server distributing version-tagged content to global edge caches](https://www.zerobuffer.io/blogs/what-is-e-tag/thumbnail.png)

# What Is an E-Tag and Why Does Caching Need It?

A browser has already downloaded your 2 MB JavaScript bundle. The file has not changed, but its cache lifetime has expired. Sending the entire bundle again wastes bandwidth; serving it blindly risks stale content. If you searched **“what is e tag,”** this is the problem the HTTP header solves.

An **ETag**, or entity tag, is an HTTP response header that identifies a specific representation of a resource. When cached content becomes stale, a browser or CDN can send that identifier back to the server; a match lets the server return `304 Not Modified` without retransmitting the response body.

That makes ETags validators, not expiration timers and not cache keys. Understanding that distinction is the difference between a cache that reuses content safely and one that either hammers the origin or serves yesterday's bytes.

## What is ETag in HTTP?

An ETag is an opaque value chosen by the server for the current representation of a resource. “Opaque” matters: clients and intermediary caches store and compare the value, but they should not infer meaning from it. [RFC 9110 defines an entity tag](https://httpwg.org/specs/rfc9110.html#field.etag) as a validator that distinguishes representations created by state changes, content negotiation, or both.

A response might look like this:

```http
HTTP/1.1 200 OK
Content-Type: application/javascript
Cache-Control: public, max-age=3600
ETag: "build-a91f7c"

...response body...
```

The quoted string is the validator. The HTTP specification does not require it to be a hash. A server can derive an ETag from a content digest, a revision number, versioned metadata, or another method—as long as the value changes whenever the server needs clients to treat the selected representation as different. [MDN's ETag reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/ETag) documents the required quoted syntax and common generation approaches.

ETag comparisons apply to a representation, not merely a URL. If `/hero.jpg` can produce AVIF, WebP, and JPEG bodies, the exact bytes may differ even though the logical image and URL are the same. Content encoding can create another representation difference. Your origin and CDN therefore need validators that stay aligned with the body actually delivered.

## How an ETag makes a conditional request work

ETag cache validation is a short request-response exchange:

1. **The first request gets the full resource.** The origin or CDN returns `200 OK`, the response body, cache instructions, and an `ETag`.
2. **The cache stores both body and validator.** While the response is fresh, the cache can normally reuse it without contacting the server.
3. **The stale response is revalidated.** The client sends the stored value in an `If-None-Match` request header.
4. **The server compares validators.** A match means the selected representation has not changed, so the server returns `304 Not Modified` with no message body. A mismatch produces a normal `200 OK` with the new body and a new ETag.
5. **The cache refreshes its metadata.** After a 304, it keeps the stored body and updates applicable response metadata before reusing it. This is the cache-refresh behavior specified by [RFC 9111](https://httpwg.org/specs/rfc9111.html#freshening.responses).

The revalidation request looks like this:

```http
GET /assets/app.js HTTP/1.1
Host: example.com
If-None-Match: "build-a91f7c"
```

If the value still matches:

```http
HTTP/1.1 304 Not Modified
Cache-Control: public, max-age=3600
ETag: "build-a91f7c"
```

The body is absent, but the request is not free. It still consumes a network round trip and server or edge work. The [HTTP semantics for `If-None-Match`](https://httpwg.org/specs/rfc9110.html#field.if-none-match) explicitly position the header as a way to update cached information with minimal transaction overhead—not as a replacement for a useful freshness lifetime.

## What is ETag's relationship to cache TTL?

A cache TTL answers **“How long may I reuse this response without checking?”** An ETag answers **“Is my stored representation still the current one?”** They cooperate, but they act at different points in a cache entry's life.

| Mechanism | Decision it controls | Typical result |
|---|---|---|
| `Cache-Control: max-age` or `s-maxage` | How long a response stays fresh | Reuse without a validation request |
| `ETag` plus `If-None-Match` | Whether a stale response still matches | `304` with no body, or `200` with new content |
| Purge or invalidation | Whether cached state should be removed or forced to revalidate now | Provider-specific cache action |

If you set `max-age=86400`, a cache can reuse the response for 24 hours without consulting the ETag. When that response becomes stale, the ETag can make the next check cheap. Setting `max-age=0, must-revalidate` instead means the response may be stored but must be validated before every reuse; that can be correct for highly mutable content, but it gives up request elimination.

The practical rule is to set the longest freshness lifetime your change tolerance permits, then add a reliable validator for the moment freshness ends. For immutable fingerprinted assets such as `app.a91f7c.js`, use long-lived caching and a changed URL for each release. For a stable URL whose body can change, use a deliberate TTL plus ETag validation. [MDN's HTTP caching guide](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching) explains how freshness, validators, and 304 responses fit together.

## Strong versus weak ETags

A strong ETag has the quoted form shown above:

```http
ETag: "build-a91f7c"
```

It asserts that two matching representations are byte-for-byte equivalent. Strong validators are appropriate when exact representation equality matters, including byte-range operations.

A weak ETag begins with the case-sensitive `W/` prefix:

```http
ETag: W/"article-revision-42"
```

It asserts semantic equivalence rather than exact byte equality. Two bodies could differ in insignificant formatting while retaining the same weak validator. Weak comparison is sufficient for `If-None-Match` cache validation, but a weak ETag cannot support every operation that requires exact equality. The [MDN ETag guidance](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/ETag#directives) notes the limitation for range requests.

Choose based on the guarantee your generator can maintain. A falsely strong ETag is worse than an honest weak one: if an intermediary compresses or transforms a response but leaves an exact validator attached to different bytes, downstream comparisons no longer mean what they claim.

![Client, edge cache, and origin comparing a resource version before reusing cached content](https://www.zerobuffer.io/blogs/what-is-e-tag/mid-article.png)

## How to implement ETags across an origin and CDN

Start with the representation boundary. List every factor that can change the response body: source revision, selected language, image format, compression, personalization, device variant, or query-driven transformation. The ETag needs to change when the delivered representation changes, and the cache key plus `Vary` behavior need to keep incompatible variants apart.

Then decide where the validator is created:

- **At the origin:** useful when the application knows the true revision or can cheaply hash the final bytes.
- **At the edge:** useful only when the CDN produces or transforms the final representation and documents how it handles validators.
- **At build time:** ideal for static assets because the deployment pipeline already knows the content version.

Do not calculate an expensive full-body hash on every dynamic request just because hashes are common. A database row version, deployment ID, or cached digest can be safer and cheaper when it maps reliably to the selected representation. The value does not need to be decodable; it needs to be stable when the representation is unchanged and different when it changes.

The CDN's job is to preserve that contract across edge locations and transformations — and many do not, rewriting or stripping validators during compression or format conversion. ZeroBuffer pairs configurable cache/TTL policies with instant purge across all 100+ edge locations, so freshness handles routine reuse, validators handle safe revalidation, and purge handles the exceptions.

Test those behaviors with your real origin headers before shifting production traffic. A feature checkbox cannot prove representation correctness, on any provider.

If an image service creates format and size variants, the [picture CDN guide](/blogs/picture-cdn) covers cache keys and `Vary` in more depth. Keep this ETag decision focused on validation rather than duplicating the entire image-variant strategy.

## How to test an ETag configuration

You can verify the core flow with `curl`. First, inspect the normal response:

```bash
curl -sS -D - -o /dev/null https://example.com/assets/app.js
```

Record `ETag`, `Cache-Control`, `Age`, `Vary`, `Content-Encoding`, and any provider cache-status header. Then repeat the request conditionally with the exact quoted ETag value:

```bash
curl -sS -D - -o /dev/null \
  -H 'If-None-Match: "build-a91f7c"' \
  https://example.com/assets/app.js
```

Expect `304 Not Modified` if the selected representation is unchanged. Next, change the underlying content and repeat the conditional request. You should receive `200 OK`, the changed body, and a different ETag.

Do not stop at one URL from one machine. Run a small matrix:

- Cold request, fresh cache hit, stale revalidation, and post-purge request
- Gzip, Brotli, and uncompressed responses when supported
- Every negotiated image or language variant
- A byte-range request for large media or downloads
- Multiple edge regions and both browser and CDN revalidation paths
- Origin success, origin timeout, and stale-serving behavior if configured

Compare headers and body hashes across the matrix. If a body changes while a strong ETag stays the same, the validator is broken. If semantically identical content gets a new ETag on every request, validation will always miss and provide no bandwidth benefit.

## Common ETag mistakes to avoid

**Treating ETag as a TTL.** An ETag does not tell a cache how long a response is fresh. Without appropriate cache directives, you can turn every view into a validation round trip.

**Reusing one strong ETag across different bytes.** Compression, image conversion, templating, and edge transformation can change a representation. Use representation-specific strong validators or a correctly generated weak validator.

**Generating unstable values.** A timestamp generated per request or a random token changes even when the body does not. The server will send `200` every time.

**Forgetting quotation marks.** Entity tags use a quoted opaque value, optionally preceded by `W/`. Fastly's current [ETag best-practices guide](https://www.fastly.com/blog/etags-what-they-are-and-how-to-use-them) calls out malformed and placeholder values as common production errors.

**Assuming every S3 ETag is a content MD5.** S3-style object APIs also use the term ETag, but its storage semantics are not a universal integrity guarantee. AWS states that the ETag returned for a completed multipart upload is [not necessarily an MD5 hash of the object](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html#mpuoverview.complete). Use explicit supported checksums for migration validation; the [S3-compatible storage guide](/blogs/s3-compatible-storage) covers that integrity concern separately.

**Ignoring write preconditions.** ETags can also prevent lost updates. `If-Match` says “perform this change only if the current validator still matches”; a mismatch can produce `412 Precondition Failed`. That is valuable for APIs, but it is a different conditional-request path from cache revalidation.

## Frequently asked questions

### What is ETag used for?

An ETag identifies a specific representation of an HTTP resource. Its most common use is validating stale cached content with `If-None-Match`, allowing an unchanged resource to produce a bodyless `304 Not Modified` response. ETags can also support conditional writes with `If-Match`.

### How does an ETag work?

The server returns an ETag with a resource, and the client stores both. On a later validation request, the client sends the value in `If-None-Match`; the server returns 304 if it still matches or 200 with a new body and validator if it does not.

### How do you generate an ETag?

Generate it from a stable property of the selected representation, such as a content hash, build ID, row revision, or cached version number. The HTTP standard does not mandate an algorithm, but the result must be quoted and must preserve the strong or weak guarantee you declare.

### Does ETag replace Cache-Control?

No. `Cache-Control` sets freshness and reuse rules, while ETag validates a stored response when a check is required. A good caching policy usually uses both: a suitable TTL to avoid unnecessary requests and a validator to avoid unnecessary body transfers after the TTL expires.

### Is an ETag always an MD5 hash?

No. HTTP treats an ETag as an opaque server-selected validator, not a required digest format. In object storage, multipart uploads and other implementation details also mean an ETag may not equal the MD5 of the full object, so use explicit checksums for data-integrity verification.

## Prove the validator before you trust it

Use an ETag when a resource can be stored and later needs a cheap, reliable change check. Use a strong validator only when you can guarantee byte equality; otherwise use a weak validator or another appropriate mechanism. Pair it with a sensible TTL, then prove the 200/304 flow across every representation your origin and CDN can deliver. Start with one mutable, high-traffic asset: inspect its headers, force revalidation, change it, and verify that the validator changes with the bytes.

If your current CDN fails that test — mangled validators, purges that take minutes, revalidation that never returns a 304 — run the same asset through [ZeroBuffer](/) instead. Configurable TTLs, instant global purge, flat $0.0049/GB, free to start with no card.
