# Max-Age

> Learn how the Max-Age cookie attribute sets expiration in seconds from now. Understand why Max-Age is preferred over Expires for reliable lifetime control.

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

> **TL;DR:** Sets cookie expiration in seconds from now (e.g., `Max-Age=3600` for 1 hour) - preferred over Expires because it's not affected by client clock issues.

## What is the Max-Age Attribute?

The **Max-Age** attribute sets how many seconds a cookie should live, starting from when it's set. It's like setting a timer - "delete this cookie in 3600 seconds (1 hour)." Unlike the Expires attribute which uses absolute dates, Max-Age uses relative time, making it immune to client clock issues.

If both are present, `Max-Age` takes precedence (RFC 6265 section 5.3, unchanged in RFC 6265bis). Browsers also cap the effective lifetime: Chrome clamps `Max-Age` and `Expires` to 400 days, as RFC 6265bis allows, so `Max-Age=31536000` (365 days) fits but `Max-Age=63072000` (2 years) will be shortened.

## How It Works

### Session Cookie (No Max-Age)

Deleted when the browser session ends, in principle. Browsers with session restore (Chrome's "Continue where you left off", Firefox's restore previous session) bring session cookies back as if the browser had never closed, so never rely on this for logout:

```http
Set-Cookie: sessionData=temp123
# Expires: When browser session ends
```

### Persistent Cookie (With Max-Age)

Deleted after specified seconds:

```http
Set-Cookie: rememberMe=true; Max-Age=2592000
# Expires: 30 days from now (2,592,000 seconds)
```

## Time Calculations

### Common Durations in Seconds

```http
# 1 minute
Max-Age=60

# 1 hour
Max-Age=3600

# 1 day
Max-Age=86400

# 1 week
Max-Age=604800

# 30 days
Max-Age=2592000

# 1 year
Max-Age=31536000
```

### Calculation Formula

```text
Seconds = Days × 24 × 60 × 60
```

## Real-World Examples

### Short-Term Authentication

Session expires in 1 hour:

```http
Set-Cookie: sessionId=abc123; Max-Age=3600; HttpOnly; Secure
```

### Remember Me Login

Keep user logged in for 30 days:

```http
Set-Cookie: rememberToken=xyz789; Max-Age=2592000; HttpOnly; Secure; SameSite=Strict
```

### Shopping Cart

Keep cart items for 1 week:

```http
Set-Cookie: cartId=cart456; Max-Age=604800; SameSite=Lax
```

### User Preferences

Store theme for 1 year:

```http
Set-Cookie: theme=dark; Max-Age=31536000; Path=/
```

### Rate Limiting

Temporary block for 15 minutes:

```http
Set-Cookie: rateLimited=true; Max-Age=900; Path=/api
```

### CSRF Token

Short-lived security token (30 minutes):

```http
Set-Cookie: csrfToken=random123; Max-Age=1800; HttpOnly; SameSite=Strict
```

## Max-Age vs Expires

### Max-Age (Preferred)

```http
Set-Cookie: data=value; Max-Age=86400
```

**Advantages:**

- Not affected by client clock changes
- Simpler to calculate
- More reliable
- Preferred by modern browsers

### Expires (Legacy)

```http
Set-Cookie: data=value; Expires=Sat, 16 Jan 2027 10:00:00 GMT
```

**Disadvantages:**

- Affected by client clock skew
- Requires date formatting
- Can fail if client time is wrong

### Priority Rules

When both are present, Max-Age takes precedence:

```http
Set-Cookie: data=value; Max-Age=3600; Expires=Sat, 16 Jan 2027 10:00:00 GMT
# Browser uses Max-Age (1 hour from now), ignores Expires
```

## Implementation Examples

### JavaScript

```javascript
// Set cookie for 24 hours
const maxAge = 24 * 60 * 60 // 86400 seconds
document.cookie = `userId=123; Max-Age=${maxAge}; Secure`
```

### Express.js

```javascript
// 7 days from now
app.post('/login', (req, res) => {
  res.cookie('sessionId', sessionId, {
    maxAge: 7 * 24 * 60 * 60 * 1000, // Express uses milliseconds!
    httpOnly: true,
    secure: true
  })
})

// Helper function for readability
const days = (n) => n * 24 * 60 * 60 * 1000

res.cookie('rememberMe', 'true', {
  maxAge: days(30), // 30 days
  httpOnly: true
})
```

### Python Flask

```python
from datetime import timedelta

@app.route('/login')
def login():
    response = make_response('Logged in')

    # 1 hour = 3600 seconds
    response.set_cookie(
        'sessionId',
        session_id,
        max_age=3600,
        httponly=True,
        secure=True
    )

    return response
```

### PHP

```php
// 30 days in seconds
$maxAge = 30 * 24 * 60 * 60; // 2,592,000

setcookie('userId', '123', [
    'max_age' => $maxAge,
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Strict'
]);
```

## Deleting Cookies with Max-Age

Set Max-Age to 0 or negative value:

```http
Set-Cookie: oldCookie=; Max-Age=0; Path=/
Set-Cookie: anotherCookie=; Max-Age=-1; Path=/
```

**JavaScript:**

```javascript
// Delete cookie immediately
document.cookie = 'sessionId=; Max-Age=0; Path=/'
```

**Express.js:**

```javascript
// Clear cookie
res.clearCookie('sessionId') // Automatically sets Max-Age=0
```

## Security Considerations

### Appropriate Lifetimes

```http
# Sensitive data: Short lifetime
Set-Cookie: sessionId=secret; Max-Age=3600; HttpOnly; Secure

# Non-sensitive data: Longer lifetime
Set-Cookie: theme=dark; Max-Age=31536000

# Critical operations: Very short lifetime
Set-Cookie: adminToken=xyz; Max-Age=900; HttpOnly; Secure
```

### Session Management

```javascript
// Different lifetimes for different security levels
const sessionLifetimes = {
  regular: 24 * 60 * 60, // 24 hours
  admin: 2 * 60 * 60, // 2 hours
  banking: 15 * 60, // 15 minutes
  api: 60 * 60 // 1 hour
}

res.cookie('sessionId', sessionId, {
  maxAge: sessionLifetimes.admin * 1000, // Express uses milliseconds
  httpOnly: true,
  secure: true
})
```

## Getting Max-Age right

**1. Use Max-Age instead of Expires:**

```http
✅ Max-Age=86400
❌ Expires=Sat, 16 Jan 2027 10:00:00 GMT
```

**2. Choose appropriate lifetimes:**

```http
Session cookies: 1-24 hours
Authentication: 1-30 days
Preferences: 30 days - 1 year
Tracking: Maximum 2 years
```

**3. Use constants for readability:**

```javascript
const HOUR = 60 * 60
const DAY = 24 * HOUR
const WEEK = 7 * DAY

res.cookie('sessionId', sessionId, {
  maxAge: 2 * HOUR * 1000, // 2 hours
  httpOnly: true
})
```

**4. Validate Max-Age values:**

```javascript
function setCookie(name, value, maxAgeSeconds) {
  // Validate reasonable limits
  if (maxAgeSeconds < 0 || maxAgeSeconds > 2 * 365 * 24 * 60 * 60) {
    throw new Error('Invalid Max-Age value')
  }

  res.cookie(name, value, { maxAge: maxAgeSeconds * 1000 })
}
```

**5. Include both for compatibility:**

```javascript
// Maximum compatibility
const maxAgeSeconds = 86400
const expires = new Date(Date.now() + maxAgeSeconds * 1000)

res.cookie('data', value, {
  maxAge: maxAgeSeconds * 1000,
  expires: expires
})
```

## Common Patterns

### Progressive Expiration

```javascript
// Extend session on activity
app.use((req, res, next) => {
  if (req.cookies.sessionId) {
    // Refresh session cookie
    res.cookie('sessionId', req.cookies.sessionId, {
      maxAge: 24 * 60 * 60 * 1000, // Reset to 24 hours
      httpOnly: true,
      secure: true
    })
  }
  next()
})
```

### Conditional Lifetimes

```javascript
// Different lifetimes based on user choice
app.post('/login', (req, res) => {
  const maxAge = req.body.rememberMe
    ? 30 * 24 * 60 * 60 * 1000 // 30 days
    : 2 * 60 * 60 * 1000 // 2 hours

  res.cookie('sessionId', sessionId, {
    maxAge: maxAge,
    httpOnly: true,
    secure: true
  })
})
```

### Environment-Based Lifetimes

```javascript
// Shorter lifetimes in development
const maxAge =
  process.env.NODE_ENV === 'production'
    ? 24 * 60 * 60 * 1000 // 24 hours in production
    : 60 * 60 * 1000 // 1 hour in development

res.cookie('sessionId', sessionId, { maxAge })
```

## Troubleshooting

### Cookie Expires Too Soon

```javascript
// Problem: Using seconds instead of milliseconds in Express
res.cookie('data', value, { maxAge: 3600 }) // Only 3.6 seconds!

// Solution: Convert to milliseconds
res.cookie('data', value, { maxAge: 3600 * 1000 }) // 1 hour
```

### Cookie Never Expires

```javascript
// Problem: Negative or zero Max-Age
Set-Cookie: data=value; Max-Age=0  // Deletes immediately

// Solution: Use positive value
Set-Cookie: data=value; Max-Age=86400  // 1 day
```

## Related Attributes

- [Expires](https://howhttpworks.com/cookies/expires) - Alternative absolute expiration method
- [Secure](https://howhttpworks.com/cookies/secure) - HTTPS-only transmission
- [HttpOnly](https://howhttpworks.com/cookies/http-only) - Prevent JavaScript access
- [SameSite](https://howhttpworks.com/cookies/same-site) - Cross-site request control
