Cookie attribute
< Set-Cookie: id=a3fWa; Max-Age=3600Max-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.
- 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
On this page
- What is the Max-Age Attribute?
- How It Works
- Session Cookie (No Max-Age)
- Persistent Cookie (With Max-Age)
- Time Calculations
- Common Durations in Seconds
- Calculation Formula
- Real-World Examples
- Short-Term Authentication
- Remember Me Login
- Shopping Cart
- User Preferences
- Rate Limiting
- CSRF Token
- Max-Age vs Expires
- Max-Age (Preferred)
- Expires (Legacy)
- Priority Rules
- Implementation Examples
- JavaScript
- Express.js
- Python Flask
- PHP
- Deleting Cookies with Max-Age
- Security Considerations
- Appropriate Lifetimes
- Session Management
- Getting Max-Age right
- Common Patterns
- Progressive Expiration
- Conditional Lifetimes
- Environment-Based Lifetimes
- Troubleshooting
- Cookie Expires Too Soon
- Cookie Never Expires
- Related Attributes
TL;DR: Sets cookie expiration in seconds from now (e.g.,
Max-Age=3600for 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:
Set-Cookie: sessionData=temp123
# Expires: When browser session ends
Persistent Cookie (With Max-Age)
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
Cookie Expires Too Soon
// 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
// 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
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
Related
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.
Domain
Learn how the Domain cookie attribute controls which domains can access cookies. Understand subdomain sharing, security implications, and restrictions.
Path
Learn how the Path cookie attribute restricts which URL paths can receive cookies. Understand path matching rules and how to scope cookies to specific routes.
Partitioned Cookies (CHIPS): The Partitioned Attribute
The Partitioned cookie attribute (CHIPS) gives third-party embeds a separate cookie jar per top-level site. Syntax, Secure requirement, and browser support.