How HTTP Works

Request header

> Cookie: session=abc123; theme=dark

Cookie Header

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

Reviewed 4 min readbeginner3 sourcesTry itMarkdown
Direction
Request
Category
Cookies
Spec
RFC 6265 §5.4
JS can set it
No: forbidden header name
On this page

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.

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/1.1 200 OK
Set-Cookie: sessionId=abc123; HttpOnly; Secure
Set-Cookie: theme=dark; Max-Age=86400

2. Browser stores and sends them back:

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

Multiple Cookies

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

With Special Characters

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

Common Use Cases

Session Management

Cookie: JSESSIONID=A1B2C3D4E5F6; PHPSESSID=xyz789

Maintains user login state across requests.

User Preferences

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

Remembers user settings and customizations.

Shopping Cart

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

Tracks items before user logs in.

Analytics and Tracking

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

Google Analytics tracking cookies.

Real-World Examples

E-commerce Site

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

Social Media Platform

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

Banking Application

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

HttpOnly

Set-Cookie: sessionId=abc123; HttpOnly

Prevents JavaScript access (XSS protection).

Secure

Set-Cookie: authToken=xyz789; Secure

Only sent over HTTPS connections.

SameSite

Set-Cookie: csrf=token123; SameSite=Strict

Controls cross-site request behavior.

Expiration

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

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

Cookie: csrfToken=random123; sessionId=user456

Include CSRF tokens to prevent cross-site attacks.

Size Limitations

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

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

Domain and Path Matching

# 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
// Browsers manage cookie storage automatically
// Cookies persist across browser sessions (unless session-only)
// Expired cookies are automatically removed

Server-Side Handling

Reading Cookies

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

// Express.js
res.cookie('sessionId', 'abc123', {
  httpOnly: true,
  secure: true,
  maxAge: 24 * 60 * 60 * 1000 // 24 hours
})
function parseCookies(cookieHeader) {
  const cookies = {}

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

  return cookies
}
// ❌ 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

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

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

# 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

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

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

Frequently asked questions

What is the Cookie header?

The Cookie header sends stored cookies from the browser to the server. It contains name=value pairs for all cookies that match the request domain and path.

How are multiple cookies sent?

Multiple cookies are sent in one header, separated by semicolons: "Cookie: session=abc; theme=dark; user=123". Each cookie is a name=value pair.

What is the difference between Cookie and Set-Cookie?

Cookie is a request header sent by clients. Set-Cookie is a response header sent by servers to create or update cookies. They work together for state management.

Why are my cookies not being sent?

Check cookie attributes: domain must match, path must match, Secure requires HTTPS, SameSite may block cross-site requests, and HttpOnly cookies are still sent but not accessible to JavaScript.

Sources

  1. MDN Web Docs: Cookiedeveloper.mozilla.org
  2. RFC 6265: HTTP Cookiesrfc-editor.org
  3. RFC 6265bis (draft): current cookie behavior (SameSite, prefixes, 400-day cap)datatracker.ietf.org

Keep going

Browse /search