How HTTP Works

Cookie attribute

< Set-Cookie: id=a3fWa; Max-Age=3600

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.

Reviewed 5 min readbeginner3 sourcesTry itMarkdown
Default if omitted
None: falls back to Expires, else a session cookie
Accepts
Seconds; 0 or negative deletes it
Sent back to server
No: never echoed in the Cookie header
Spec
RFC 6265 §4.1.2.2, RFC 6265bis (draft)
On this page

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

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:

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

Deleted after specified seconds:

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

Time Calculations

Common Durations in Seconds

# 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

Seconds = Days × 24 × 60 × 60

Real-World Examples

Short-Term Authentication

Session expires in 1 hour:

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

Remember Me Login

Keep user logged in for 30 days:

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

Shopping Cart

Keep cart items for 1 week:

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

User Preferences

Store theme for 1 year:

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

Rate Limiting

Temporary block for 15 minutes:

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

CSRF Token

Short-lived security token (30 minutes):

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

Max-Age vs Expires

Max-Age (Preferred)

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)

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:

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

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

Express.js

// 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

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

// 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:

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

JavaScript:

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

Express.js:

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

Security Considerations

Appropriate Lifetimes

# 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

// 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:

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

2. Choose appropriate lifetimes:

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

3. Use constants for readability:

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:

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:

// 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

// 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

// 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

// 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

// 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
// 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
  • Expires - Alternative absolute expiration method
  • Secure - HTTPS-only transmission
  • HttpOnly - Prevent JavaScript access
  • SameSite - Cross-site request control

Frequently asked questions

What is the Max-Age cookie attribute?

Max-Age specifies cookie lifetime in seconds from when the browser receives it. Max-Age=3600 means the cookie expires in 1 hour.

What is the difference between Max-Age and Expires?

Max-Age uses relative seconds, Expires uses absolute dates. Max-Age is simpler and avoids timezone issues. Max-Age takes precedence if both are set.

What does Max-Age=0 do?

Max-Age=0 or negative values delete the cookie immediately. Use this to log users out or clear cookie data.

What is a session cookie?

Cookies without Max-Age or Expires are session cookies. They are discarded when the browser session ends, although browsers with session restore can bring them back after a restart. Use them for temporary data that should not persist.

Sources

  1. MDN Web Docs: Cookie Max-Agedeveloper.mozilla.org
  2. RFC 6265: Max-Age Attributerfc-editor.org
  3. RFC 6265bis (draft): cookie lifetime limitdatatracker.ietf.org

Keep going

Browse /search