Expires
response headercaching
The HTTP/1.0 way to say when a response goes stale. Cache-Control: max-age overrides it.
Expires holds a date; until then, caches may reuse the response without asking. It predates Cache-Control and has two weaknesses: it depends on the client's clock being right, and it has to be recalculated for every response. When Cache-Control: max-age or s-maxage is present, caches ignore Expires.
Syntax
Expires: <http-date>
Defined in RFC 9111 §5.3 — Expires.
Examples
Expires: Thu, 15 Oct 2026 12:00:00 GMT
stale after this moment
Expires: 0
an invalid date, which caches treat as already expired
Good to know
- Prefer
Cache-Control: max-age; Expires only matters for very old HTTP/1.0 caches. - Any invalid value, including
0, means already expired. - nginx's
expiresdirective sets both Expires andCache-Control: max-age.
Further reading: HTTP caching explained
How to set Expires
location /static/ {
expires 30d; # sets Expires and Cache-Control: max-age=2592000
}ExpiresActive On ExpiresByType image/png "access plus 1 month" ExpiresByType text/css "access plus 1 week"
// Cache-Control does the real work; Expires is for HTTP/1.0 caches
res.set("Cache-Control", "public, max-age=3600");
res.set("Expires", new Date(Date.now() + 3600 * 1000).toUTCString());Check it
Fetch only the response headers and filter for Expires. In the browser, it's under Response Headers in DevTools → Network.
curl -sI https://example.com/ | grep -i '^expires:'
To compare response headers across many URLs at once, use the HTTP Status Checker.
Frequently asked questions
Do I need Expires if I send Cache-Control?
No. Every cache in use today understands Cache-Control, and max-age takes precedence over Expires when both are present.