Etag Generation
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
- What is the fastest ETag generation strategy?
- Why wrap ETags in double quotes?
- What is a weak ETag prefix?
- How does version-based ETag differ from content hash?
- When would you use last-modified over ETag?
Answers:
- Version-based (using database row version or incrementing counter).
- HTTP specification requires quoted-string format for ETags.
W/prefix indicates content is semantically but not byte-identical.- Version-based uses a counter; content hash uses content fingerprint.
- 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
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro