Expires: the older way to set a freshness lifetime
Expires gives an absolute date and time after which a cached response is stale. Cache-Control max-age, a relative value, overrides it wherever both are present.
At a glance
- Direction
- Response only.
- Defined in
- RFC 9111
- When absent
- Freshness comes from Cache-Control, or from a cache heuristic based on Last-Modified.
- Format
- An HTTP date in GMT, for example Wed, 21 Oct 2026 07:28:00 GMT
- Overridden by
- Cache-Control max-age, and by s-maxage in shared caches
- Invalid dates
- Any unparseable value, including the common Expires: 0, means already stale.
- Still useful for
- Very old clients and intermediaries that predate Cache-Control
In detail
Expires names an absolute moment, which makes it fragile in a way max-age is not. The client compares that moment against its own clock, so a machine whose time is wrong caches for the wrong length, and a response sitting in a shared cache for an hour expires at the same instant for everyone regardless of when they received it.
That is why the relative directive won. max-age counts forwards from when the response was generated, so it survives a wrong client clock and behaves the same for a visitor arriving now as for one who arrived an hour ago. Where both headers are present, a cache that understands Cache-Control must ignore Expires entirely.
The value Expires: 0 appears in a great deal of old configuration. It is not a valid HTTP date, and the specification requires caches to read any invalid date, that one included, as a time already in the past. The effect is what the author intended, but writing an explicit past date or a Cache-Control directive states it more clearly.
It is still worth sending alongside Cache-Control when your traffic includes very old clients or corporate proxies. The two must agree: an Expires date a week away next to max-age=60 is not a compromise, it is a contradiction that different intermediaries will resolve differently.
Examples
Cache-Control: max-age=3600
Expires: Wed, 21 Oct 2026 07:28:00 GMT
Date: Wed, 21 Oct 2026 06:28:00 GMTThe two agree here. Caches that understand Cache-Control use the max-age and ignore the Expires line.
Expires: 0
Pragma: no-cache
Cache-Control: no-cache, no-storeOnly the Cache-Control line is doing real work in any current client.
Things that catch people out
- A far future Expires with no fingerprint in the filename means visitors keep the old file until the date arrives, and there is no way to reach out and invalidate it.
- The date is compared against the client's clock, so a device with the wrong time can treat a fresh response as stale or a stale one as fresh.
- Setting Expires without also setting Cache-Control leaves your caching policy at the mercy of every intermediary's interpretation of dates.
Try it: HTTP Headers Checker
Check a live URL or paste headers you already have. It grades six security headers from A+ to F and lists every other header the response returned.
HTTP Headers Checker
LOCAL · Browser-OnlyView HTTP response headers for any URL
Enter any website URL to fetch and analyze its HTTP response headers. We'll automatically add https:// if needed.
Why Check Security Headers?
Security headers protect your website against common attacks like XSS, clickjacking, and MIME sniffing. They're set by your web server and cost nothing to implement. Missing security headers are one of the most common findings in security audits.
Related HTTP headers
All http headers →Cache-Control: directives for browsers and shared caches
Every Cache-Control directive that matters, the difference between no-cache and no-store, and how to cache immutable assets forever without breaking deploys.
Date: when the response was generated
What the Date header records, the exact GMT format required, how caches use it with Age and max-age, and the one case where a server may omit it.
Age: how long a cached response has been sitting there
What the Age header counts, how it combines with max-age to give remaining freshness, and how to use it to tell a cache hit from a miss at the edge.
Last-Modified: timestamps as a weak validator
The exact date format Last-Modified requires, why one second resolution makes it a weak validator, and how caches use it to guess freshness.
Vary: which request headers change the response
How Vary extends a cache key, why Vary: User-Agent destroys hit rates, what Vary: * means, and the CORS case where forgetting Vary: Origin causes real bugs.
If-None-Match: asking whether anything changed
How If-None-Match produces a 304, why it uses weak comparison, how it overrides If-Modified-Since, and its second job preventing duplicate creates.
Tools for this
- HTTP Headers Checker Free online HTTP headers checker - view HTTP response headers for any URL
- HTTP Request Builder Free online HTTP request builder - build and test HTTP requests with custom headers and body