An ETag is a response validator that lets a client ask whether a selected representation has changed.
Python Flask ETag responses: preserve cache validators across conditional reads
Operation contract
The owned Flask endpoint returns a fixed receipt summary, marks it private with a short cache lifetime, sets an ETag for those exact representation bytes and applies the request’s conditional headers. A normal local test-client GET returns the payload and validator. A second GET sends the same If-None-Match and receives 304 with no body. A mismatched validator receives 200 and the representation. All three requests remain inside the local test client.
Failure and ownership boundary
A validator is not permission to read a resource. A real endpoint must perform authentication and tenant/resource checks before deciding which representation is selected, even when it might return 304. This fixture has no identity service or persistent record changes. A production validator must change when the represented bytes change and must account for representation variants. Django REST Framework list visibility: filter the principal’s rows before paging, Flask error responses: preserve status without exposing internal exception text and Flask JSON API: reject unknown fields, booleans and oversized bodies remain necessary.
Tested environment
Dependency check: this program was executed on CPython 3.14.6 with Flask==3.1.3. Install these versions in a separate virtual environment. The download includes the recorded environment snapshot; no third-party package is part of the website runtime.
Working program
from flask import Flask, Response, request
application = Flask(__name__)
@application.get("/receipts/41")
def receipt_summary():
response = Response(b"receipt=41;amount=125\n", mimetype="text/plain")
response.cache_control.private = True
response.cache_control.max_age = 30
response.add_etag()
return response.make_conditional(request)
with application.test_client() as client:
initial = client.get("/receipts/41")
validator = initial.headers["ETag"]
unchanged = client.get("/receipts/41", headers={"If-None-Match": validator})
changed = client.get("/receipts/41", headers={"If-None-Match": '"different"'})
print("statuses:", initial.status_code, unchanged.status_code, changed.status_code)
print("304 body:", unchanged.data)
print("validator retained:", unchanged.headers["ETag"] == validator)
print("cache policy:", initial.headers["Cache-Control"])Output
statuses: 200 304 200
304 body: b''
validator retained: True
cache policy: private, max-age=30Costs and limits
Computing the validator reads the representation bytes. Avoid reconstructing an expensive report merely to discover it is unchanged when a correctly maintained version can identify the same representation. This tiny response does not measure proxy behavior, compression variants, bandwidth savings or production cache hit rate.
Common Mistakes
- Run access checks before returning an unchanged-resource status.
- Do not reuse a validator when the selected representation bytes change.
Connected lessons
Flask JSON API: reject unknown fields, booleans and oversized bodies, Django REST Framework list visibility: filter the principal’s rows before paging, Flask error responses: preserve status without exposing internal exception text.
Trace the next boundary
Python Flask If-Match: reject a stale in-memory revision before mutation.
