skip to content

A page already has three cookies. What happens when script runs `document.cookie = "theme=dark; path=/; max-age=3600"`, and which parts of that string can ever be read back?

level: middleimportance: must knowfreq 63%

answer

  1. writes one cookie, not the whole string
  2. other cookies survive untouched
  3. attributes are write-only
  4. omitting path is not site-wide
  5. no exception when the write is rejected

basics

~20 s

Assigning to document.cookie adds or replaces exactly one cookie and leaves the other three untouched — it is not a normal property assignment. Only the name=value part is ever readable afterwards; path and max-age are write-only.

solid answer

~50 s

The setter is not a normal assignment: it parses the string as a single `Set-Cookie`-style directive and applies it to the store. So this writes or overwrites the cookie named `theme` and leaves the other three exactly as they were — there is no way to clear the jar by assigning an empty string. Everything after the first `;` is attributes for that one cookie: `path=/` scopes it, `max-age=3600` makes it persist an hour instead of dying with the session. You can also set `domain`, `expires`, `secure`, `samesite` and `partitioned` here. Reading `document.cookie` afterwards returns only `theme=dark` among the pairs — the attributes never come back. And the write is silent: an over-long value, a disallowed attribute, or a cookie count over the browser's per-domain cap is simply dropped, with no exception and no return value to check.

code

javascript · 11 lines
javascript
// Before: "a=1; b=2; c=3"
document.cookie = 'theme=dark; path=/; max-age=3600';
console.log(document.cookie); // "a=1; b=2; c=3; theme=dark"

// Only the first pair is a cookie; the rest are attributes for it.
document.cookie = 'x=1; y=2';
console.log(document.cookie.includes('y=2')); // false — y was read as an attribute

// Assigning empty string clears nothing.
document.cookie = '';
console.log(document.cookie); // still "a=1; b=2; c=3; theme=dark; x=1"

go deeper

for a junior

Remember that each assignment sets one cookie and leaves the others alone, and that the parts after the first semicolon are attributes for that cookie, not more cookies.

for a middle

Explain the getter/setter asymmetry precisely, name the attributes script may set, and say why an omitted path scopes the cookie to the current directory rather than the site root.

for a senior

Demonstrate that you treat cookie writes as unverifiable best-effort: read back when it matters, keep values small against the per-cookie and per-domain caps, and centralise writes in one helper that always sets path explicitly.

for a principal

Own the convention across teams — who may write cookies from script at all, which values must instead be server-set so they can be HttpOnly, and how the per-request byte cost of the jar is budgeted.

## The setter is a command, not an assignment The single most surprising thing about `document.cookie` is that its getter and setter are asymmetric. Reading gives you the whole jar; writing operates on **one** cookie. The string you assign is parsed the way the browser parses a `Set-Cookie` response header value: a `name=value` pair, then optional `;`-separated attributes. ```js document.cookie; // "a=1; b=2; c=3" document.cookie = 'theme=dark; path=/; max-age=3600'; document.cookie; // "a=1; b=2; c=3; theme=dark" ``` Because of this, several intuitions that come from ordinary properties are wrong: - `document.cookie = ''` does **not** empty the jar. It is a malformed directive and does nothing useful. - You cannot write two cookies in one assignment. `"a=1; b=2"` sets `a` to `1` and treats `b=2` as an unrecognised attribute, which is ignored. - Assigning the same name again replaces that cookie's value *if the path and domain also match*; with a different path you get a second, separate cookie of the same name. ## Which attributes you may set from script The writable set is `Path`, `Domain`, `Expires`, `Max-Age`, `Secure`, `SameSite` and `Partitioned`. Attribute names are case-insensitive. Two are worth calling out because they change the cookie's lifetime: - **No `Expires` and no `Max-Age`** produces a *session cookie*: it lives in memory for the browsing session and disappears when the browser decides the session is over. - `Max-Age=3600` is a relative lifetime in seconds; `Expires` takes an absolute HTTP date string. When both are present, `Max-Age` wins. One attribute is deliberately **not** settable from script: `HttpOnly`. A cookie written by `document.cookie` can never be HttpOnly, which is the whole point — only the server can create a cookie script must not read. ## Defaults you inherit if you omit `path` Omitting `path` does not mean "site-wide". The default path is derived from the current document's URL — effectively the directory portion of it. Writing a cookie from `/app/settings` without a path scopes it to `/app`, and code running at `/` will not see it. This is the number-one cause of "my cookie vanished when the user navigated", so write `path=/` deliberately whenever you want site-wide scope. The `Domain` default is similar: omitting it produces a *host-only* cookie for exactly the current host, while setting `domain=example.com` widens it to subdomains. You can only widen to a domain the current document is within; a write naming an unrelated domain is rejected. ## The write is silent, and that matters There is no return value, no promise, and no exception. If the browser rejects the write — value over the ~4 KB budget, per-domain cookie cap exceeded, `Secure` requested from an insecure page, a `Domain` you are not entitled to — nothing happens and your code proceeds as if it succeeded. The only way to check is to read `document.cookie` back and look for the pair, and even that cannot confirm the attributes took effect: ```js document.cookie = 'theme=dark; path=/; max-age=3600'; if (!document.cookie.split('; ').some((p) => p.startsWith('theme='))) { // write was rejected — quota, or a Secure/Domain problem } ``` ## Encoding is your job A raw `;` inside a value terminates the pair and turns the remainder into garbage attributes; `,` and whitespace are likewise unsafe. Always `encodeURIComponent` the value (and the name, if it is dynamic) on write and decode on read. The API performs no escaping of its own. ## The round-trip asymmetry, stated plainly Everything you write except `name=value` is invisible afterwards. There is no `document.cookie` query for "what path is this on" or "when does it expire". That asymmetry drives real design decisions: if your code needs to know a cookie's expiry, it must encode that into the value or keep it in another store — and it is the main reason the promise-based cookie API, which returns objects carrying the attributes, exists at all.

  • Your code writes a cookie from a page at `/app/settings` without a `path` attribute. Where is it readable?
    Only under `/app`, because the default path is derived from the current document's URL rather than defaulting to the site root. Navigate to `/` or `/checkout` and the cookie is filtered out of `document.cookie` and not sent on those requests. Always write `path=/` explicitly when you mean site-wide; the default is a scope decision you should not leave implicit.
  • Can script create an HttpOnly cookie by including `httponly` in the string?
    No. `HttpOnly` is not in the set of attributes the `document.cookie` setter honours, and browsers ignore it there. A cookie that script must not read can only be created by the server via `Set-Cookie`. That asymmetry is deliberate: if script could mint HttpOnly cookies, the flag would give no guarantee about which cookies script has ever handled.
  • How do you know whether a cookie write actually succeeded?
    Only by reading `document.cookie` back and checking that the pair appears; the setter returns nothing and throws nothing. Even then you have confirmed the name and value only — you cannot verify that `Secure`, `SameSite` or the expiry were applied, because the getter never returns attributes. Treat cookie writes as best-effort and keep values small.

saying these in an interview costs you the question

  • Thinks assigning to document.cookie replaces the entire cookie string
  • Believes one assignment can set several cookies at once
  • Expects an exception or false return when the write is rejected
  • Assumes an omitted path means the whole site
  • Thinks script can set HttpOnly on a cookie it writes

context