How does the `Path` attribute on an HTTP cookie decide when the browser sends it, and why is Path not treated as a security boundary?
answer
- prefix match on whole segments, not substring
- /admin matches /admin/users, not /administration
- default Path = directory of the setting URL
- same-origin policy ignores path → no isolation
- use a separate host for real separation
basics
~20 sPath is a prefix match on directory segments: Path=/admin sends the cookie to /admin and anything under it. It is not a security boundary because same-origin JavaScript from any path can read and write cookies for other paths.
solid answer
~50 sThe browser sends a cookie when the request path *path-matches* the cookie's `Path`: either identical, or the request path starts with `Path` followed by `/` (with a special case when `Path` already ends in `/`). So `Path=/admin` matches `/admin`, `/admin/`, `/admin/users` — but not `/administration`, because the match is on whole segments, not raw string prefix. If `Path` is omitted the browser derives a default from the request URI's directory, which is a common source of surprise; set `Path=/` explicitly. It is **not** a security boundary because the same-origin policy does not partition by path. A script running at `/app/page` can do `document.cookie = 'sid=x; Path=/admin'` to write into another path, and can read cookies from another path by navigating an iframe or simply by the fact that many frameworks scope everything to `/`. Path is a *scoping and size* tool — keep an admin-only cookie off asset requests — not an isolation mechanism. Real isolation needs a separate host.
code
http · 4 linesGET /admin -> Cookie: adm=1
GET /admin/users -> Cookie: adm=1
GET /administration -> (no adm cookie)
GET /Admin -> (no adm cookie)go deeper
Explain segment-wise prefix matching with an example, and that leaving Path out gives a surprising directory-based default.
Add the default-path derivation, case sensitivity, duplicate-name ordering, and that the same-origin policy ignores path so scripts can write any path.
Argue the boundary point concretely — fixation via document.cookie, same-origin iframes — and recommend a separate host with __Host- cookies for privileged surfaces.
Decide the hosting topology: whether admin and tenant surfaces get their own origins, and what that costs in DNS, TLS, SSO and deployment versus the isolation it actually buys.
## The matching rule Each stored cookie has a path. On every request the browser compares the request's path component against it, and sends the cookie when any of these hold: - the paths are identical; or - the cookie's path is a prefix of the request path **and** the cookie path ends with `/`; or - the cookie's path is a prefix of the request path **and** the first character of the remainder is `/`. That third clause is why matching is segment-aware. With `Path=/admin`: - `/admin` → sent (identical) - `/admin/users` → sent (remainder starts with `/`) - `/administration` → **not** sent (remainder starts with `i`) - `/Admin` → **not** sent (path matching is case-sensitive) - `/other` → not sent Query strings and fragments are not part of the path and never affect matching. ## The default is a trap If `Set-Cookie` omits `Path`, the browser computes a *default path* from the request URI: take the path, drop everything after the last `/`. A cookie set by a response to `/account/settings` defaults to `Path=/account`, so it will not be sent on `/dashboard` or on `/`. Teams hit this as "the cookie exists in devtools but is not sent", and it is one of the most common cookie bugs. Always set `Path=/` explicitly unless you have a specific reason not to — and note that the `__Host-` name prefix *requires* `Path=/`. Also remember that Path is combined with domain scoping, not a substitute for it: a cookie matches only if the host matches *and* the path matches. And the `Cookie` header sends only names and values, so if two cookies with the same name exist at different paths, both are sent, ordered by longest path first — and the server usually reads whichever its parser sees first, which is a real source of "stale value keeps coming back" bugs. ## Why Path is not a security boundary The browser's isolation primitive is the **origin** — scheme, host, port — and it deliberately does not include the path. Everything that follows from that undermines path-based cookie isolation: **Scripts can write any path.** A page at `https://app.example.com/user/profile` runs `document.cookie = "session=attacker; Path=/admin"`. The browser accepts it: there is no rule that a page may only set cookies for its own path. So a compromised low-privilege page can plant or overwrite cookies in a "protected" path — cookie fixation, again invisible to the server since attributes are not echoed back. **Scripts can read other paths.** Even with no direct API, same-origin code can create an iframe or a same-origin `fetch`/`XMLHttpRequest`, or simply `window.open` a URL under the target path, and reach `document.cookie` in that document context because it is the same origin. HttpOnly blocks direct reading, but that protection comes from HttpOnly, not from Path. **Everything else is shared anyway.** Same-origin pages share `localStorage`, `sessionStorage`, IndexedDB, service-worker scope decisions and the DOM. If an attacker has script execution anywhere on the origin, path separation buys nothing. ## What Path is genuinely good for - **Request-size hygiene.** Scoping a large admin or upload cookie to `/admin` keeps it off every image and API request, which matters given cookies are re-sent on every matching request and header limits are real. - **Avoiding name collisions** between independent apps mounted under different paths on one host — though this is fragile precisely because either app can write the other's path. - **Making intent readable** in a codebase. What it is not good for: separating tenants, separating privilege levels, or protecting an admin session from a user-facing page. If you need those, put the sensitive surface on its own **host** — `admin.example.com` with host-only, `__Host-`-prefixed cookies — so the origin boundary, which the browser actually enforces, is doing the work.
- A cookie appears in devtools but is not sent on requests to `/`. What is the most likely cause?The Set-Cookie omitted the Path attribute, so the browser derived a default path from the setting URL's directory — for example /account for a response to /account/settings. The cookie is stored but only sent under that prefix. Setting Path=/ explicitly fixes it.
- Two cookies named `sid` exist, one at Path=/ and one at Path=/admin. What does the server receive on GET /admin?Both are sent in one Cookie header, conventionally ordered with the longer path first, and only names and values are transmitted so the server cannot tell them apart. Most server parsers take the first occurrence, which makes behaviour depend on browser ordering. Avoid duplicate names across paths entirely.
Path is a label on a shared filing cabinet drawer, not a lock. Everyone in the room can open any drawer; the label just tells them where things live.
saying these in an interview costs you the question
- Treating Path=/admin as protection against a compromised /user page
- Believing Path matching is a raw string prefix, so /administration matches /admin
- Omitting Path and being surprised the cookie is not sent site-wide
- Thinking the server can see which path a received cookie was scoped to
- Using Path instead of a separate host to isolate an admin surface