Skip to content

Etag Generation

DodaTech 2 min read

title: "ETag Generation — Content Hashing and Version-Based ETags" description: "ETag generation uses content hashing or version counters to produce unique response identifiers for conditional requests, enabling efficient cache validation." date: 2026-06-28 lastmod: 2026-06-28 weight: 24 tags: [apis, caching] }

ETag generation creates unique response identifiers using content hashes, version counters, or last-updated timestamps for efficient HTTP cache validation.

What You'll Learn

  • Content hash ETags
  • Version-based ETags
  • Choosing the right ETag strategy

Why It Matters

Efficient ETag generation minimizes server work during conditional requests. A good ETag strategy reduces validation overhead to near zero.

Code Examples

import hashlib
import json

# Content hash ETag
@app.route('/api/products')
def get_products():
    products = db.get_products()
    # Generate ETag from sorted JSON content
    content = json.dumps(products, sort_keys=True)
    etag = hashlib.sha256(content.encode()).hexdigest()

    if request.headers.get('If-None-Match') == etag:
        return '', 304

    response = jsonify(products)
    response.headers['ETag'] = etag
    response.headers['Cache-Control'] = 'public, max-age=300'
    return response

# Version-based ETag (row version column)
@app.route('/api/product/<int:id>')
def get_product(id):
    product = db.execute(
        "SELECT id, name, price, version FROM products WHERE id = ?", [id]
    )
    etag = f'"{product["version"]}"'

    if request.headers.get('If-None-Match') == etag:
        return '', 304

    response = jsonify(product)
    response.headers['ETag'] = etag
    return response

# Last-modified based fallback
def compute_etag_from_timestamp(last_modified):
    return f'"{int(last_modified.timestamp())}"'

# Weak ETag for list endpoints
def compute_weak_etag(data):
    # W/ prefix indicates semantically equivalent content
    return f'W/"{hashlib.md5(str(len(data)).encode()).hexdigest()}"'
// ETag from database row version
app.get('/api/products/:id', async (req, res) => {
  const product = await db.query(
    'SELECT *, row_version FROM products WHERE id = $1',
    [req.params.id]
  );

  const etag = `"${product.row_version}"`;

  if (req.headers['if-none-match'] === etag) {
    return res.status(304).end();
  }

  res.set('ETag', etag);
  res.json(product);
});

Common Mistakes

1. Slow ETag Generation

Hashing large responses on every request defeats caching benefits.

2. Non-Deterministic ETags

If the same content produces different ETags, conditional requests never match.

3. ETag Without Quotation Marks

All ETag values must be wrapped in double quotes (except weak ETags).

4. Ignoring Content Encoding

ETag should account for Content-Encoding if the response differs.

5. Version-Based ETag Without Atomic Increment

Race conditions can assign the same version to different content.

Practice Questions

  1. What is the fastest ETag generation strategy?
  2. Why wrap ETags in double quotes?
  3. What is a weak ETag prefix?
  4. How does version-based ETag differ from content hash?
  5. When would you use last-modified over ETag?

Answers:

  1. Version-based (using database row version or incrementing counter).
  2. HTTP specification requires quoted-string format for ETags.
  3. W/ prefix indicates content is semantically but not byte-identical.
  4. Version-based uses a counter; content hash uses content fingerprint.
  5. When exact content comparison isn't needed and second granularity is fine.

Challenge: Implement version-based ETags using PostgreSQL row versioning or SQLite rowid. Benchmark ETag generation vs content hashing.

FAQ

Can ETags be predicted without generating the response?

: Yes, with version-based ETags from database metadata.

How long do ETags remain valid?

: Until the content changes. ETags don't expire; they change.

What is the maximum ETag length?

: No specified limit, but keep under 1KB for performance.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro