HTTP Cache Expiry Calculator
Enter the Cache-Control, Expires, Date and Age values of a response, the time it was received and the time to check. The calculator gives the freshness lifetime, the current age and whether the response is still fresh.
What this calculator does
It covers one part of RFC 9111, the HTTP caching standard: the freshness calculation. That is the part that decides whether a stored response may be reused without checking with the origin server. Validation (ETag, If-Modified-Since, 304), Vary and request-side Cache-Control are not covered.
Freshness is not a refresh and not a deletion
A response being stale only means that a cache may not reuse it without checking first. It does not mean that the browser screen refreshes by itself, and it does not mean that the file on the origin server is changed or deleted. Likewise, a response being fresh does not mean the file on the server has not changed. The Expires header, the standard says, "does not imply that the original resource will change or cease to exist at, before, or after that time". This calculator only works out the time arithmetic of the cached copy.
Freshness lifetime and current age
- Freshness lifetime: the length of time the response can stay "fresh". It is set by the response itself (Cache-Control or Expires) and does not change as time passes.
- Current age: the time that has passed since the response was generated. It grows as time passes.
- Is it fresh? The response is fresh only when the freshness lifetime is greater than the current age (when they are equal, it is stale).
- Remaining freshness = freshness lifetime − current age.
Which value sets the freshness lifetime
Following section 4.2.1 of RFC 9111, the first match is used:
| Order | Shared cache (CDN, proxy) | Private cache (browser) |
|---|---|---|
| 1 | s-maxage | (s-maxage is not used) |
| 2 | max-age | max-age |
| 3 | Expires − Date | Expires − Date |
| 4 | If none is present, there is no explicit expiration | Same |
- If max-age is present, Expires is ignored. In a shared cache, if s-maxage is present, max-age and Expires are ignored (section 5.3).
- If the same directive appears more than once, the first one is used (one of the approaches the standard allows).
- A max-age or s-maxage that is not an integer, or is in quotes (the standard says a sender must not generate the quoted form), is treated as invalid, and the response is considered stale (freshness lifetime 0). A value above 2,147,483,648 is treated as 2,147,483,648 (section 1.2.2).
- If Expires is
0or a date that does not exist, the response is treated as already expired (section 5.3). - When there is no explicit expiration, heuristic freshness (for example an estimate from Last-Modified) is not calculated. The standard leaves the heuristic lifetime to the implementation.
How the current age is calculated
The formulas are from section 4.2.3 of RFC 9111:
- Apparent age = max(0, time received − Date)
- Age when received = max(apparent age, Age header + response delay)
- Current age = age when received + time since it was received
This calculator does not ask for the time the request was sent, so it takes the response delay (the time from sending the request to receiving the response) as 0. On a connection with a large delay, the calculated age can be smaller than the real one. The standard also allows using the Age header plus the delay alone.
Rules for the input
- Enter dates as an IMF-fixdate (for example
Sun, 06 Nov 1994 08:49:37 GMT) or as a UTC time like2026-10-21 07:28:00. Everything is in UTC (GMT); time zones are not handled. The year must be from 1900 to 2100, and the seconds from 0 to 59 (a leap second, 60, is not accepted). The day name is not checked against the date. - The RFC 850 and asctime formats are formats that HTTP recipients are expected to accept, but this calculator does not support them. (That does not mean they are invalid in HTTP. Convert the value to an IMF-fixdate.)
- If Date is empty, the time you enter as "Time the response was received" is used in place of Date. RFC 9110 says that a recipient that receives a response without Date records the time of receipt and adds it as the Date. If that time is empty too, the calculations that need Date (Expires − Date and the apparent age) cannot be done. The browser's current time is never used automatically; it is filled in only when you press the "current time" button, into the "Time to check" field.
- The time to check must not be earlier than the time the response was received.
Other Cache-Control directives
They are not used in the calculation, but they are shown as notes about reuse.
- no-store: must not be stored. private: a shared cache must not store it (a browser may).
- no-cache (without arguments): even while fresh, it cannot be reused without validation.
- must-revalidate: once stale, it must not be used without validation. proxy-revalidate: the same, but it does not apply to a private cache.
- public: explicitly allows storing.
- immutable (RFC 8246) and stale-while-revalidate and stale-if-error (RFC 5861) are outside RFC 9111. They are recognized but not used in the calculation.
Examples
| Input | Result |
|---|---|
| max-age=3600, no Age, 1,800 s after it was received | Freshness lifetime 3,600 s, current age 1,800 s, 1,800 s remaining (fresh) |
| The same, at exactly 3,600 s | Current age 3,600 s. It equals the freshness lifetime, so it is stale |
| max-age=3600, Age=600, just received | Current age 600 s, 3,000 s remaining |
| Shared cache with s-maxage=60, max-age=3600 | Freshness lifetime 60 s (max-age is ignored) |
Things to know
- This is a calculation based on the standard. Real browsers and CDNs do not always behave exactly as the standard describes. Actions such as a forced reload and CDN-specific settings can give different results. The behavior of a particular product is not guaranteed.
- Heuristic freshness, validation, Vary, request directives and adding up several caches are not covered.
Sources and checks
The following was checked against the full text of the standards on November 1, 2026:
- RFC 9111, "HTTP Caching": https://www.rfc-editor.org/rfc/rfc9111 (sections 1.2.2, 4.2.1, 4.2.3, 5.1, 5.2.2 and 5.3, including the freshness test
freshness_lifetime > current_age) - RFC 9110, "HTTP Semantics": https://www.rfc-editor.org/rfc/rfc9110 (sections 5.6.7 and 6.6.1)
This calculator follows those sections for freshness only. Passing this calculation is not a claim that a product or a server conforms to the standard.
Frequently asked questions
What happens when both max-age and Expires are present?
max-age takes priority and Expires is ignored (RFC 9111, section 5.3).
Does s-maxage apply to a browser?
s-maxage is a directive for shared caches. A private cache (a browser) does not use it.
What is the Age header?
It is the number of seconds, written by a cache along the way, since the response was generated or last checked at the origin. This calculator uses it to find the current age.
Does a stale response disappear or refresh by itself?
No. Stale only means a cache must check before reusing it. It does not refresh the screen or change anything on the origin server.
How can I check a time?
The Unix Timestamp Converter and the Cron Expression Calculator may help.