# Set-Cookie Header: Attributes, Examples and Browser Rules

> Set-Cookie syntax and every attribute: Expires, Max-Age, Domain, Path, Secure, HttpOnly, SameSite, Partitioned, __Host- prefixes, with Express and Django.

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

> **TL;DR:** `Set-Cookie` tells the browser to store a name=value pair and replay it in `Cookie` on matching requests. For a session cookie use `HttpOnly; Secure; SameSite=Lax; Path=/`, and prefer the `__Host-` name prefix.

## Syntax

```http
HTTP/1.1 200 OK
Set-Cookie: __Host-sid=a1b2c3d4; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=86400
```

The browser stores it and later sends:

```http
GET /dashboard HTTP/1.1
Host: example.com
Cookie: __Host-sid=a1b2c3d4
```

One cookie per header. Send several `Set-Cookie` lines, never one comma-joined line.

## Attributes

| Attribute | Effect |
| --- | --- |
| `Max-Age=N` | Lifetime in seconds from now. `0` or negative deletes the cookie. Takes precedence over `Expires` |
| `Expires=<HTTP-date>` | Absolute expiry in GMT. Dates in the past delete the cookie |
| `Domain=example.com` | Also sends the cookie to all subdomains. Omit it to restrict to the exact host that set it |
| `Path=/` | URL path prefix the cookie applies to. It is not a security boundary |
| `Secure` | Only sent over HTTPS (browsers also treat `localhost` as secure for this purpose) |
| `HttpOnly` | Hidden from `document.cookie`, still sent on requests. Limits damage from XSS, does not prevent it |
| `SameSite=Strict\|Lax\|None` | Controls sending on cross-site requests. `None` requires `Secure` |
| `Partitioned` | Opts into CHIPS: the cookie is keyed to the top-level site that embedded it |

Expiry. A cookie with neither `Max-Age` nor `Expires` is a session cookie, but "deleted when the browser closes" is unreliable: Chrome's "Continue where you left off" and similar restore features keep session cookies alive. Use an explicit short `Max-Age` for anything with security meaning. RFC 6265bis limits lifetime to 400 days and Chrome clamps longer values.

Pick expiry dates well in the future when you need an absolute date, for example:

```http
Set-Cookie: theme=dark; Path=/; Expires=Wed, 01 Dec 2027 12:00:00 GMT; SameSite=Lax
```

A copy-pasted `Expires` from an old tutorial is already in the past, and the cookie is deleted the moment it is set.

## SameSite and "site"

"Site" means the registrable domain (eTLD+1): `app.example.com` and `api.example.com` are same-site, even though they are different origins. `example.com` and `example.co.uk` are not. A request is cross-site when the top-level site differs.

When `SameSite` is absent, Chromium-based browsers treat the cookie as `Lax`, with a short exception that allows it on top-level cross-site POSTs for the first two minutes. Firefox and Safari do not apply that Lax default, though Safari blocks third-party cookies outright and Firefox partitions them. Always write the attribute explicitly. `SameSite=None; Secure` is required for cookies in third-party contexts (embedded widgets, cross-site iframes), and many browsers now block or partition those anyway. Details in [SameSite](https://howhttpworks.com/cookies/same-site).

## Prefixes and Partitioned

Name prefixes are enforced by the browser:

```http
Set-Cookie: __Host-sid=abc; Path=/; Secure; HttpOnly; SameSite=Lax
Set-Cookie: __Secure-pref=1; Domain=example.com; Secure
```

`__Secure-` requires `Secure` and an HTTPS origin. `__Host-` additionally requires `Path=/` and no `Domain`, so a compromised or unrelated subdomain cannot overwrite your session cookie. A cookie violating the prefix rules is silently rejected.

`Partitioned` (CHIPS) gives embedded third-party widgets cookie state that is separate per top-level site:

```http
Set-Cookie: __Host-widget=xyz; Path=/; Secure; SameSite=None; Partitioned
```

## Limits that bite

- About 4096 bytes per cookie (name plus value plus attributes); browsers drop larger ones silently. Practical per-domain count limits are around 180 in Chrome and Firefox.
- All matching cookies are sent on every request, including static assets. Large cookies produce `431 Request Header Fields Too Large` or nginx `400 Request Header Or Cookie Too Large`.
- The `Secure` flag cannot be set from an HTTP page, and an HTTP response cannot overwrite a `Secure` cookie in current browsers.

## By stack

Express's `maxAge` is milliseconds, Django's `max_age` and Flask's `max_age` are seconds, and Next.js `cookies().set` takes seconds. Mixing these up is the most common reason a cookie lasts 1000 times too long or too short.

```python
from flask import make_response

@app.route('/login')
def login():
    response = make_response('Logged in')
    response.set_cookie(
        'sessionId', 'abc123',
        max_age=86400, secure=True, httponly=True, samesite='Lax'
    )
    return response
```

nginx can add cookies and pass them through, and `proxy_cookie_flags` rewrites flags on upstream cookies:

```nginx
proxy_cookie_flags ~ secure httponly samesite=lax;
```

## Debug a cookie that does not stick

```bash
curl -si https://example.com/login -d 'user=a&pass=b' | grep -i '^set-cookie'
curl -s -c jar.txt -b jar.txt https://example.com/login -d 'user=a&pass=b'
cat jar.txt
```

In Chrome DevTools, Application, then Cookies shows stored cookies; the Network panel marks rejected ones with a warning icon on the Set-Cookie header and a reason such as "This attempt to set a cookie via a Set-Cookie header was blocked because it had the Secure attribute but was not received over a secure connection." Typical causes: `Secure` over HTTP, `SameSite=None` without `Secure`, `Domain` not matching the host, a `__Host-` rule violation, or a size over 4096 bytes. See [Cookie not sent](https://howhttpworks.com/debug/cookie-not-sent).

## Related

- [Cookie](https://howhttpworks.com/headers/cookie), [Cache-Control](https://howhttpworks.com/headers/cache-control), [Vary](https://howhttpworks.com/headers/vary)
- [Expires](https://howhttpworks.com/cookies/expires), [Max-Age](https://howhttpworks.com/cookies/max-age), [Secure](https://howhttpworks.com/cookies/secure), [HttpOnly](https://howhttpworks.com/cookies/http-only), [Domain](https://howhttpworks.com/cookies/domain), [Path](https://howhttpworks.com/cookies/path)
- [Cookie security guide](https://howhttpworks.com/guides/cookie-security) and the [Cookie builder](https://howhttpworks.com/tools/cookie-builder)
- [Cookies vs localStorage vs sessionStorage](https://howhttpworks.com/compare/cookies-vs-localstorage): where to keep state and auth tokens in the browser.
