# Cookie Header

> Learn how the Cookie header sends stored cookies to servers with each request. Understand cookie transmission, session management, and security considerations.

Source: https://howhttpworks.com/headers/cookie
Last reviewed: 2026-10-04

> **TL;DR:** Sends stored cookies from browser to server for session management and user tracking. Contains name=value pairs separated by semicolons for state persistence.

## What is Cookie?

The **Cookie** header sends previously stored cookies back to the server. It's like showing your membership card or loyalty points when you visit a store—the server recognizes you and remembers your preferences.

Cookies enable stateful interactions in the stateless HTTP protocol, allowing servers to remember users across requests.

## How Cookies Work

**1. Server sets cookies:**

```http
HTTP/1.1 200 OK
Set-Cookie: sessionId=abc123; HttpOnly; Secure
Set-Cookie: theme=dark; Max-Age=86400
```

**2. Browser stores and sends them back:**

```http
GET /dashboard HTTP/1.1
Host: example.com
Cookie: sessionId=abc123; theme=dark
```

## Cookie Format

### Single Cookie

```http
Cookie: name=value
```

### Multiple Cookies

```http
Cookie: sessionId=abc123; userId=42; theme=dark; lang=en
```

### With Special Characters

```http
Cookie: message=Hello%20World; data=%7B%22key%22%3A%22value%22%7D
```

## Common Use Cases

### Session Management

```http
Cookie: JSESSIONID=A1B2C3D4E5F6; PHPSESSID=xyz789
```

Maintains user login state across requests.

### User Preferences

```http
Cookie: theme=dark; language=en-US; timezone=America/New_York
```

Remembers user settings and customizations.

### Shopping Cart

```http
Cookie: cart=item1,item2,item3; cartTotal=49.99
```

Tracks items before user logs in.

### Analytics and Tracking

```http
Cookie: _ga=GA1.2.123456789; _gid=GA1.2.987654321
```

Google Analytics tracking cookies.

## Real-World Examples

### E-commerce Site

```http
GET /products HTTP/1.1
Host: shop.example.com
Cookie: sessionId=user123; cart=prod1,prod2; currency=USD; lastVisit=2026-01-17
```

### Social Media Platform

```http
GET /feed HTTP/1.1
Host: social.example.com
Cookie: authToken=jwt123; userId=42; notifications=enabled; darkMode=true
```

### Banking Application

```http
GET /account HTTP/1.1
Host: bank.example.com
Cookie: secureSession=encrypted123; csrfToken=abc456; lastActivity=1642518000
```

## Cookie Attributes (Set by Server)

### HttpOnly

```http
Set-Cookie: sessionId=abc123; HttpOnly
```

Prevents JavaScript access (XSS protection).

### Secure

```http
Set-Cookie: authToken=xyz789; Secure
```

Only sent over HTTPS connections.

### SameSite

```http
Set-Cookie: csrf=token123; SameSite=Strict
```

Controls cross-site request behavior.

### Expiration

```http
Set-Cookie: remember=true; Max-Age=2592000
Set-Cookie: temp=data; Expires=Wed, 20 Jan 2027 12:00:00 GMT
```

## Security Considerations

### Sensitive Data Protection

```javascript
// ❌ Don't store sensitive data in cookies
document.cookie = 'password=secret123'
document.cookie = 'creditCard=1234567890123456'

// ✅ Store session identifiers only
document.cookie = 'sessionId=abc123; Secure; HttpOnly'
```

### CSRF Protection

```http
Cookie: csrfToken=random123; sessionId=user456
```

Include CSRF tokens to prevent cross-site attacks.

### Size Limitations

```javascript
// Cookies are limited to ~4KB per cookie
// Total limit: ~4KB per domain
const largeCookie = 'data=' + 'x'.repeat(4000) // Might be rejected
```

## Browser Behavior

### Automatic Sending

Browsers automatically include relevant cookies:

```http
GET /api/profile HTTP/1.1
Host: example.com
Cookie: sessionId=abc123; theme=dark
# Browser adds these automatically
```

### Domain and Path Matching

```http
# Cookie set for .example.com applies to:
# - example.com
# - www.example.com
# - api.example.com

# Cookie set for /admin applies to:
# - /admin
# - /admin/users
# - /admin/settings
```

### Cookie Jar Management

```javascript
// Browsers manage cookie storage automatically
// Cookies persist across browser sessions (unless session-only)
// Expired cookies are automatically removed
```

## Server-Side Handling

### Reading Cookies

```javascript
// Node.js/Express
app.get('/profile', (req, res) => {
  const sessionId = req.cookies.sessionId
  const theme = req.cookies.theme || 'light'

  if (!sessionId) {
    return res.status(401).json({ error: 'Not authenticated' })
  }

  const user = getUserBySession(sessionId)
  res.json({ user, theme })
})
```

### Setting Cookies

```javascript
// Express.js
res.cookie('sessionId', 'abc123', {
  httpOnly: true,
  secure: true,
  maxAge: 24 * 60 * 60 * 1000 // 24 hours
})
```

### Parsing Cookie Header

```javascript
function parseCookies(cookieHeader) {
  const cookies = {}

  if (cookieHeader) {
    cookieHeader.split(';').forEach((cookie) => {
      const [name, value] = cookie.trim().split('=')
      cookies[name] = decodeURIComponent(value)
    })
  }

  return cookies
}
```

## Getting Cookie right

### Minimize Cookie Size

```javascript
// ❌ Large cookies slow down requests
Cookie: userData = {
  name: 'John',
  email: 'john@example.com',
  preferences: { theme: 'dark', lang: 'en' }
}

// ✅ Store minimal identifiers
Cookie: sessionId = abc123
```

### Use Appropriate Attributes

```javascript
// For authentication
res.cookie('sessionId', id, {
  httpOnly: true,
  secure: true,
  sameSite: 'strict'
})

// For preferences
res.cookie('theme', 'dark', { maxAge: 30 * 24 * 60 * 60 * 1000 }) // 30 days

// For tracking (if needed)
res.cookie('analytics', id, { secure: true, sameSite: 'lax' })
```

### Handle Missing Cookies

```javascript
app.get('/api/data', (req, res) => {
  const sessionId = req.cookies.sessionId

  if (!sessionId) {
    return res.status(401).json({
      error: 'Authentication required',
      redirectTo: '/login'
    })
  }

  // Continue with authenticated request
})
```

## Testing Cookies

### Using curl

```bash
# Send cookies manually
curl -H "Cookie: sessionId=abc123; theme=dark" https://example.com/api/profile

# Save cookies from response
curl -c cookies.txt https://example.com/login

# Use saved cookies
curl -b cookies.txt https://example.com/dashboard
```

### Using JavaScript

```javascript
// Read cookies (if not HttpOnly)
const cookies = document.cookie.split(';').reduce((acc, cookie) => {
  const [name, value] = cookie.trim().split('=')
  acc[name] = value
  return acc
}, {})

// Set cookies (client-side)
document.cookie = 'theme=dark; max-age=86400; secure'
```

## Cookie Size Limits and Cookie Prefixes

Browsers enforce size limits on cookies. A single cookie is limited to about 4096 bytes, and browsers drop larger ones silently. Per-domain count limits are around 180 in Chrome and Firefox (the old figure of 50 is obsolete). Cookies over the limit are silently dropped, which causes subtle login bugs. Everything the browser sends back in the `Cookie` header also counts against the server's request-header limits, which is where `431 Request Header Fields Too Large` and nginx's `400 Request Header Or Cookie Too Large` come from; the [request header too large guide](https://howhttpworks.com/debug/request-header-too-large) shows how to find the oversized cookie.

Cookie prefixes are a security mechanism that enforces attribute requirements. A cookie named `__Secure-session` must have the `Secure` attribute — browsers reject it otherwise. A cookie named `__Host-session` must have `Secure`, must not have a `Domain` attribute, and must have `Path=/`. The `__Host-` prefix is the stronger of the two because it prevents subdomain cookies from overriding it, making it ideal for session cookies on applications that share a domain with other services.

## Related Headers

- [Set-Cookie](https://howhttpworks.com/headers/set-cookie) - Server sets cookies
- [Authorization](https://howhttpworks.com/headers/authorization) - Alternative authentication
- [User-Agent](https://howhttpworks.com/headers/user-agent) - Client identification
- [Referer](https://howhttpworks.com/headers/referer) - Request origin tracking
