# Expires

> Learn how the Expires cookie attribute sets an absolute expiration date. Understand date formats, timezone handling, and when to use Expires vs Max-Age.

Source: https://howhttpworks.com/cookies/expires
Last reviewed: 2026-10-04

> **TL;DR:** `Expires` sets an absolute HTTP-date after which the browser discards the cookie. If `Max-Age` is also present it wins, browsers cap either one at about 400 days, and a date in the past deletes the cookie instantly, which is why copy-pasted example dates silently break.

## What Expires does

Without `Expires` or `Max-Age` a cookie is a session cookie. Do not read that as "gone when the browser closes": Chrome's "Continue where you left off" and Firefox/Safari session restore bring session cookies back across restarts, so a session cookie is not a reliable short lifetime. Use an explicit lifetime for anything that must expire.

```http
Set-Cookie: remember=7f3a9c; Expires=Fri, 15 Oct 2027 12:00:00 GMT; Path=/; Secure; HttpOnly; SameSite=Lax
```

The date uses the IMF-fixdate form of HTTP-date (RFC 9110 section 5.6.7): `Day, DD Mon YYYY HH:MM:SS GMT`. Always GMT, English day and month names. Browsers are lenient and also parse the older cookie date grammar from RFC 6265 section 5.1.1, but do not rely on it.

Dates in examples on this page are in the future relative to October 2026. If you copy an old snippet with a 2025 date, the browser receives an already-expired cookie and removes it, so the symptom is "Set-Cookie is in the response but the cookie never appears in DevTools".

## Precedence and limits

- If `Max-Age` and `Expires` both appear, `Max-Age` takes precedence (RFC 6265bis section 5.5). Sending both for "compatibility" is only needed for very old clients such as IE 8 and earlier, which ignored `Max-Age`.
- RFC 6265bis caps cookie lifetime at 400 days. Chrome enforces this: a larger `Expires` or `Max-Age` is clamped to 400 days from the time it is set. Other browsers have their own caps, for example Safari limits cookies written by JavaScript to 7 days under Intelligent Tracking Prevention in some contexts.
- `Expires` is evaluated against the client's clock. A device with a wrong clock may see the cookie as already expired. `Max-Age` avoids that.

## Generating the date

```javascript
// Express: both forms work; maxAge is milliseconds in Express
res.cookie('remember', token, { expires: new Date('2027-10-15T12:00:00Z'), httpOnly: true, secure: true })
res.cookie('remember', token, { maxAge: 30 * 24 * 60 * 60 * 1000, httpOnly: true, secure: true })

// Browser: document.cookie wants a UTC string
document.cookie = `theme=dark; Expires=${new Date(Date.now() + 365 * 864e5).toUTCString()}; Path=/; Secure; SameSite=Lax`
```

```python
# Django: pass max_age (seconds) or a timezone-aware expires datetime
response.set_cookie('theme', 'dark', max_age=60 * 60 * 24 * 365, secure=True, samesite='Lax')
```

```php
// PHP: 'expires' is a Unix timestamp, not a date string
setcookie('userId', '123', [
    'expires' => time() + 30 * 24 * 60 * 60,
    'path' => '/',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Lax',
]);
```

## Deleting a cookie

Send the same name, `Path` and `Domain` with a past date (or `Max-Age=0`). A mismatched `Path` or `Domain` creates a second cookie instead of deleting the first.

```http
Set-Cookie: remember=; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/
```

```javascript
res.clearCookie('remember', { path: '/' })
```

## Troubleshooting

| Symptom | Likely cause |
| --- | --- |
| Cookie appears then vanishes immediately | `Expires` in the past, or `Max-Age=0`/negative |
| Lifetime shorter than requested | Browser cap (400 days in Chrome) or ITP limits on script-set cookies |
| Cookie lifetime differs per user | Client clock skew with `Expires`; switch to `Max-Age` |
| Delete does nothing | `Path` or `Domain` does not match the original cookie |
| Parsed date wrong | Local timezone name instead of GMT, or `new Date('2027-10-15 12:00 PST')` style strings; use ISO `Z` strings in JavaScript |

Inspect what the server really sent:

```bash
curl -sI https://example.com/login | grep -i '^set-cookie'
```

## Related

- [Max-Age](https://howhttpworks.com/cookies/max-age)
- [Secure](https://howhttpworks.com/cookies/secure)
- [HttpOnly](https://howhttpworks.com/cookies/http-only)
- [SameSite](https://howhttpworks.com/cookies/same-site)
- [Set-Cookie header](https://howhttpworks.com/headers/set-cookie)
