Summary

  • HTTP Age is an estimate of seconds since an origin generated or successfully validated a response. It is not the creation date of the resource, document or bytes represented by that response.
  • Each cache combines an inherited Age, the origin's Date, request and response timing, network delay and local residence, then replaces the field with one updated current_age when it reuses the stored response.
  • Freshness is a separate decision: current age is compared with a lifetime derived from cache directives, Expires or a permitted heuristic. The scalar neither proves validation nor tells the recipient whether reuse is allowed.

A small number can describe an old document

Imagine that a policy page was written three years ago, validated by its origin two minutes ago and then served from a cache. The response can legitimately arrive with Age: 120. Nothing in that value says the prose is 120 seconds old. It says the cache estimates that roughly 120 seconds have elapsed since the origin generated or successfully validated the response now being reused.

That distinction is easy to lose because ordinary speech gives “age” to objects. HTTP gives it to a response. The resource identified by a URL may predate the Web server. The representation bytes may have been unchanged through many validations. A cache entry may have been replaced, updated or evicted. Age is not a history of any of those things. It is a compact timing value for a specific cache calculation.

This was not decorative metadata. A cache needed to decide whether it could answer a new request with an earlier response without contacting the origin. To make that choice across several independently operated intermediaries, it needed an estimate that survived the hand-off from one cache to the next.

HTTP/1.0 had cache practice but no shared response age

The 1996 HTTP/1.0 specification described caching, dates and expiration, but its defined response-header set did not contain Age. A recipient could see Date or Expires, use local receipt time and apply implementation policy. It did not receive a standardized scalar carrying the time a response had already accumulated elsewhere.

That gap matters in a chain. Suppose cache A holds a response for 80 seconds and then sends it to cache B. If B begins counting from zero, it grants the response a second youth. If B relies only on the origin's wall-clock Date, clock skew can make the response appear younger or older than it is. A common rule had to preserve elapsed time without assuming that every machine shared a perfect clock.

HTTP/1.1 supplied that rule in 1997. Age travelled with a cached response as the sender's estimate. The early specification stated the crucial revalidation boundary explicitly: after successful revalidation, age was based on the time of revalidation, not on the original response. The field therefore belonged to cache reuse, not to archival chronology.

The estimate came from two imperfect views

A receiving cache could estimate initial age in two independent ways. First, it could compare the origin's Date with the local time at which the response arrived. In the current notation:

apparent_age = max(0, response_time - date_value)

The maximum with zero prevents a clock ahead at the origin from producing negative age. This view uses two clocks and can be distorted when they disagree.

Second, the cache could trust the Age value received from upstream, then add the time consumed by the request-response exchange:

response_delay = response_time - request_time

corrected_age_value = age_value + response_delay

This view does not require subtracting the origin clock from the cache clock. But it depends on upstream caches correctly inserting and updating Age. A path containing older or defective implementations can understate what has already elapsed.

The conservative compatibility calculation takes the larger view:

corrected_initial_age = max(apparent_age, corrected_age_value)

Modern HTTP permits the corrected age value to be used directly where obsolete caches that omit Age are not a concern. The historical maximum remains revealing. The protocol did not pretend either observation was metaphysically true. It specified how a local implementation could combine evidence without electing one remote clock as sovereign.

Residence made the number grow locally

Once the response was stored, the cache knew something no upstream actor could know in advance: how long this particular copy remained resident before the next request. It calculated:

resident_time = now - response_time

current_age = corrected_initial_age + resident_time

When the cache used that response without validation, it did not append another age or preserve a list of previous values. It generated one Age and replaced the field received from upstream with its computed current_age. The next cache inherited the result, added its observed response delay and later added its own residence.

The scalar therefore accumulated duration while shedding route detail. It could say that time had passed across transit and storage, but not where most of it passed. Two caches with long residence and ten caches with brief residence could emit the same number. The field contained no cache names, hop list, receipts or custody signatures.

That is why Age is useful and limited at the same time. A downstream recipient can continue the calculation without obtaining every upstream log. It cannot reconstruct a provenance graph from the final integer.

Response age and freshness lifetime answered different questions

The most common conceptual error is to read Age: 120 as either “fresh for 120 seconds” or “120 seconds remain.” It means neither. Age answers how much estimated time has elapsed. Freshness lifetime answers how long reuse without validation is permitted under the applicable cache rules.

The decision compares the two:

response_is_fresh = (freshness_lifetime > current_age)

The lifetime can come from a shared-cache s-maxage, a max-age, the interval between Expires and Date, or, where allowed, a heuristic derived from other response metadata. Origin policy supplies some inputs; the cache chooses and executes the applicable rule. A current age of 120 seconds is fresh under a 300-second lifetime and stale under a 60-second lifetime.

Even “stale” is not a moral verdict on the bytes. It means the ordinary freshness comparison failed. Validation may freshen the stored response. Applicable directives or disconnected-operation rules may allow stale reuse. Conversely, a young response is not automatically reusable if storage or request constraints prohibit it. The number, lifetime and reuse policy are separate layers.

Revalidation made the same bytes young in a precise sense

Suppose a cache stores a representation for a day, sends a conditional request and receives confirmation that it remains valid. The underlying article can still be a day, a year or a decade old. The stored bytes may be identical. Yet the response's cache age can be based on that successful validation.

This is not falsifying history. HTTP is answering an operational question: how long since the origin last generated or confirmed the response that the cache proposes to reuse? The distinction permits a stable representation to remain efficient without presenting the age field as a publication timestamp.

It also establishes an evidence boundary. A lower post-validation age does not prove that the content changed. A higher age does not prove that the origin is unavailable. Age carries neither validator details nor the outcome record that justified updating stored metadata. Those facts must be observed separately.

Presence was evidence of a cache, but absence was not evidence of origin

Current HTTP permits a narrower inference: an Age field means the response is not first-hand; a cache generated it from stored state. The field alone does not reveal whether that cache validated with the origin during this request or relied on an earlier validation.

The converse is deliberately denied. A response without Age does not prove that the origin was contacted. Older intermediaries might omit it; implementations can be non-compliant; an observer might see a transformed or incomplete record. Absence cannot be upgraded into a first-hand certificate.

The field is also syntactically modest. It is a non-negative integer in seconds. Invalid values should be ignored by a cache. If a delta-seconds value is too large to represent, or arithmetic overflows, modern specifications preserve the historical saturation convention of 2147483648, or the greatest convenient positive integer. That sentinel is practical infinity, not evidence of an exactly measured 68-year cache stay.

The durable record is larger than the header

An operator trying to explain a cache incident needs more than the final emitted integer. The evidence should preserve the selected cache key and variant, the received Date and Age, request and response timestamps from the same local clock, every derived term, and the source of the freshness lifetime. It should also record whether validation occurred, which status and validator metadata returned, and whether the decision path was fresh reuse, validated reuse, permitted stale reuse or failure.

Keeping only Age: 120 collapses several causes into one result. The same number might reflect inherited residence, transit delay, local storage or an apparent-age correction caused by clock difference. A graph that shows only the scalar can reveal a symptom, but it cannot allocate responsibility.

HTTP's design avoided the opposite excess as well. It did not require a central chronology service through which every cache decision had to pass. The origin contributed Date, policy and validators. Each cache selected a stored response, measured local elapsed time, calculated current age and accepted the consequence of reuse. The next recipient could repeat the arithmetic.

The common field coordinated. It did not rule. Its authority extended only to an estimate that running implementations could consume, check against other inputs and replace. That is why the number survived: it was small enough to move through the network, but narrow enough not to become the object's identity.

Sources