💬 qa

What Is an ETag? A Plain-English Q&A

Common questions about HTTP ETags answered in plain language — fingerprints for cached content, If-None-Match, 304 Not Modified, strong vs weak validators, and how they fit with Cache-Control.

What Is an ETag? A Plain-English Q&A

ETags show up in browser DevTools and CDN docs — but what *are* they? Here are common questions about HTTP ETags, answered in plain language. Literacy only: how caching validators work, not how to attack anything.


What is an ETag?


An ETag (entity tag) is a short fingerprint the server attaches to a specific version of a resource — a page, a JSON payload, an image, a stylesheet.


Think of it as a version sticker on a box. If the sticker is the same next time you check, the contents haven't changed. If the sticker is different, something inside is new.


Example response header:


HTTP/1.1 200 OK

ETag: "a1b2c3d4"

Cache-Control: max-age=3600


That "a1b2c3d4" value is opaque to the browser. You don't decode it. You just send it back later so the server can say "same" or "different."


What does ETag stand for?


ETag is short for entity tag.


  • **Entity** = the body of the HTTP response (the bytes you care about)
  • **Tag** = a label that identifies that exact version of those bytes

  • It's defined in the HTTP caching model alongside other validators like Last-Modified. The important idea: validators let caches *check* freshness without always re-downloading the whole response.


    Related: What Is Caching? — temporary copies and why they matter.


    How do ETags help with caching?


    Browsers and CDNs often keep a local copy of a response. Later, before using that copy (or when Cache-Control says they must revalidate), they ask the origin: "Is my copy still current?"


    With an ETag, that question becomes:


  • Client already has a response with `ETag: "a1b2c3d4"`
  • Client sends a **conditional request** with that value
  • Server compares fingerprints
  • If they match → **304 Not Modified** (keep your copy; no body)
  • If they differ → **200 OK** with the new body and a new ETag

  • You save bandwidth and latency when nothing changed. When something did change, you get a fresh copy.


    What is If-None-Match?


    If-None-Match is the request header that carries the ETag(s) the client already has.


    GET /styles.css HTTP/1.1

    Host: example.com

    If-None-Match: "a1b2c3d4"


    Reading it in plain English: "Give me this resource unless none of these tags still match — in which case just confirm it's unchanged."


    If the server's current ETag is still "a1b2c3d4", it answers:


    HTTP/1.1 304 Not Modified

    ETag: "a1b2c3d4"


    No response body. The cache keeps using what it already stored.


    If the resource changed:


    HTTP/1.1 200 OK

    ETag: "e5f6g7h8"

    Cache-Control: max-age=3600


    /* new CSS bytes here */


    Status codes like 304 sit alongside the everyday ones covered in HTTP Status Codes Decoded.


    Strong vs weak ETags — what's the difference?


    HTTP distinguishes two kinds of validators:


    Strong ETag (default look):


    ETag: "abc123"


    A strong match means the byte-for-byte representation is identical. Safe for partial-content / range-style use cases where exact bytes matter.


    Weak ETag (prefixed with W/):


    ETag: W/"abc123"


    A weak match means "semantically the same" — good enough for many caches even if the exact bytes differ slightly (for example, different compression or trivial formatting). Weak validators are for freshness checks, not for proving identical bytes.


    Rule of thumb for literacy:


  • **Strong** = same bytes
  • **Weak** = same meaning for caching purposes

  • Most of the time you'll just see quoted strings in DevTools and treat them as opaque fingerprints.


    How is an ETag different from Last-Modified?


    Both are validators. They answer "has this changed?" in different ways.


    | Validator | Based on | Example |

    |-----------|----------|---------|

    | ETag | Fingerprint of the representation | ETag: "v42.9f3a" |

    | Last-Modified | Timestamp | Last-Modified: Mon, 28 Sep 2026 00:00:00 GMT |


    Clients use:


  • `If-None-Match` with ETags
  • `If-Modified-Since` with last-modified times

  • ETags are often more precise: two edits in the same second can share a timestamp but get different ETags. Timestamps are simple and widely supported. Many servers send both.


    How does ETag fit with Cache-Control?


    They solve different jobs:


  • **`Cache-Control`** (and friends like `max-age`, `no-cache`, `no-store`) decide *whether* and *how long* a response may be stored, and when it must be rechecked.
  • **ETag** (or `Last-Modified`) gives the cache a *cheap way to recheck* without re-sending the whole body.

  • Important literacy traps (also covered in our Cache-Control posts):


  • `no-cache` does **not** mean "don't store." It usually means "you may store this, but revalidate before reuse." ETags shine here: revalidate with `If-None-Match`, often get 304.
  • `no-store` means don't keep a copy — there's nothing left to validate later.

  • Deep dives:


  • [no-cache vs no-store: What's the Difference?](/blog/no-cache-vs-no-store)
  • [5 Cache-Control Directives Worth Knowing](/blog/5-cache-control-directives-worth-knowing)

  • What happens when content changes?


    When the origin updates the resource, it should issue a new ETag. Caches that still hold the old fingerprint will fail the match on the next conditional request and download the new representation.


    Until that revalidation happens, a cache might still serve the old copy if max-age (or similar) says it's fresh. Validators don't magically push updates to every browser; they make *checks* efficient when a check is due.


    CDNs add another layer of copies at the edge — see What Is a CDN Edge Cache? and Checklist: Do You Need a CDN?.


    Do I need ETags if I already set max-age?


    Not always. If a file is immutable and fingerprinted in the URL (like app.a1b2c3.js with a long max-age), browsers may never need to revalidate until the URL itself changes.


    ETags help most when:


  • The URL stays the same across updates
  • You use `no-cache` or short freshness lifetimes and want cheap revalidation
  • Bandwidth matters and content often hasn't changed

  • If every request must skip stored copies entirely, that's a Cache-Control / no-store decision — not something an ETag alone solves.


    Quick reference


    | Term | What it means |

    |------|---------------|

    | ETag | Fingerprint (entity tag) for a response version |

    | If-None-Match | Request header sending stored ETag(s) for a check |

    | 304 Not Modified | "Your copy is still good; no body attached" |

    | Strong ETag | Byte-identical match ("abc") |

    | Weak ETag | Semantic match (W/"abc") |

    | Last-Modified | Time-based validator alternative |

    | Cache-Control | Rules for storing and revalidating — ETag helps the revalidate step |




    Want to go deeper?


    What Is Caching? — browser, CDN, and application caches


    Browser Cache: Hard Refresh Explained — when you intentionally bypass stored copies


    no-cache vs no-store: What's the Difference? — revalidate vs don't store


    5 Cache-Control Directives Worth Knowing — max-age, s-maxage, and friends


    HTTP Status Codes Decoded — where 304 fits




    *This post is part of Hacking Bits, where we explain how everyday technology works — one bit at a time.*


    Related Posts