Web Platform · Browser Storage
Cookie Scope and the Attributes That Restrict Sending
How the Domain and Path attributes decide which requests carry a cookie, and how Secure, HttpOnly, and expiry constrain what can be done with it once stored.
A cookie is not a key-value pair with a URL attached. It is a record whose storage identity is the triple of name, domain, and path, with a value and flags attached, and every request the browser makes is matched against the whole set. Getting the matching rules right explains why a cookie set at /admin is not visible to /admin-panel, why a cookie from app.example.com reaches api.example.com only when Domain says so, and why two cookies of the same name can be sent in one request.
Storage identity is the name, domain, and path triple
RFC 6265 defines a cookie’s key as the tuple of name, domain, and path. Values and attributes are the payload; they are replaced wholesale when a Set-Cookie header arrives with a matching tuple, and the old record is removed and the new one inserted. This is why an application can hold a cookie called session at path / and another called session at path /app without conflict — they are two records — and why clearing one with an empty value and a past expiry leaves the other untouched.
Because the key includes domain and path but not port, a cookie set by https://example.com:8443 is sent to https://example.com:9443 as well. Cookies have never been port-scoped; the Set-Cookie syntax has no port attribute. Everything the storage key article says about origins splitting on port does not apply here.
Domain: host-only by default, suffix-matched when asked
When Set-Cookie carries no Domain attribute, the resulting cookie is host-only: it is sent only to the exact host that set it. shop.example.com receiving a host-only cookie will not send it to www.example.com, and it will not send it to example.com.
Adding Domain=example.com converts it into a domain cookie, and the matching rule becomes a suffix test from RFC 6265 section 5.1.3: the request host must equal the domain, or the request host must end with the domain preceded by a dot, and the request host must not be an IP address. So Domain=example.com matches example.com, shop.example.com, and a.b.example.com, but not notexample.com, because the dot boundary is required. The browser refuses a Domain value that the setting host does not itself domain-match, which prevents evil.example from setting a cookie for example.com. It also refuses public suffixes: Domain=co.uk is rejected because a registrable-domain check would otherwise let anyone with a co.uk subdomain write cookies for every other one.
The practical effect is that domain cookies leak sideways. Any subdomain, including one that serves content posted by users or an abandoned marketing microsite, can read and overwrite a domain cookie for the parent, which is why session cookies are commonly set host-only even when other cookies are domain-wide.
Path matching is a prefix test with a boundary
Path matching (section 5.1.4) is a string operation on the request path. A cookie with path P matches request path R when any of these holds:
PandRare identical;Pis a prefix ofRand the last character ofPis/;Pis a prefix ofRand the character inRimmediately after the prefix is/.
This is why /admin does not match /admin-panel: the prefix test passes but neither boundary condition does, since the next character is - and the cookie path does not end in a slash. /admin does match /admin/ and /admin/users. The default, when the attribute is absent or does not begin with a slash, is the default-path: the request path up to but excluding its rightmost /, or / when there is only one slash. A cookie set from https://example.com/app/page with no Path therefore gets path /app, not /.
HTTP/1.1 200 OK
Set-Cookie: sid=abc123; Path=/; Secure; HttpOnly; SameSite=Lax
Set-Cookie: theme=dark; Max-Age=2592000; Path=/app
Set-Cookie: promo=1; Domain=example.com; Path=/; Expires=Wed, 01 Jan 2025 00:00:00 GMT
Path scoping is not a security boundary. Script running at any path on the host can navigate to or fetch a resource under another path, and the browser attaches matching cookies to those requests regardless of which document initiated them. The attribute reduces what is sent on ordinary requests (an image under /static/ need not carry the session cookie) and it separates same-named records, but it does not stop a hostile script on the same host from probing.
Attributes that constrain rather than scope
Expires and Max-Age control lifetime. Max-Age is a delta in seconds and takes precedence when both appear; Expires is an absolute HTTP date. A cookie with neither is a session cookie, deleted when the browser decides the session has ended, and browsers differ on when that is: some preserve session cookies across a restart with a restore-session feature, so a session cookie is not a reliable guarantee of deletion. Setting Expires to a date in the past, or Max-Age to zero or a negative value, deletes the record.
Secure restricts transmission to secure channels. It does not encrypt or hide the value from script. A browser ignores a Secure cookie received over an insecure channel, so a site that sets a session cookie over plain HTTP and relies on the attribute ends up with no cookie at all.
HttpOnly removes the cookie from document.cookie and from any script-visible API. It is a defence against cookie theft through cross-site scripting; it does not stop an attacker from issuing authenticated requests from a script running in the page, because the browser still attaches the cookie. The flag is one-directional: the server sets it, script cannot.
SameSite is not scoping but request eligibility, and it is the attribute that most often causes “the cookie is missing on the redirect” reports. SameSite=Strict withholds the cookie on any cross-site request, including an ordinary link click from another site, so a user arriving via a link appears logged out until they navigate again within the site. SameSite=Lax sends it on top-level cross-site navigations that use safe methods, so a link click arrives authenticated but a cross-site POST form submission does not carry it. SameSite=None restores cross-site sending and requires Secure in current browsers. When the attribute is absent, modern browsers default to Lax; the default was historically no restriction, and code written against the old default depends on cookies being sent on cross-site subresource requests.
Two name prefixes, defined in the revision to the cookie specification, make certain combinations non-negotiable. A cookie whose name begins with __Host- must have Secure, must have Path=/, and must not carry Domain; a name beginning with __Secure- must have Secure. Browsers reject a Set-Cookie that violates the prefix rules, which turns a silent misconfiguration into a cookie that simply never appears.
Limits, ordering, and partitioned records
RFC 6265 requires implementations to support at least 4096 bytes per cookie, counting name, value, and attributes; at least 50 cookies per domain; and at least 3000 cookies in total. Implementations commonly hold to a 4096-byte ceiling and silently drop a cookie that exceeds it — a failure that shows up as a missing session rather than an error. When a domain is at its per-domain limit, RFC 6265 leaves the eviction policy to the implementation and recommends removing expired records first; browsers generally fall back to least-recently-used within the domain.
When several cookies match a request, the browser orders them by path length, longest first, and among equal-length paths by creation time. The Cookie header is a single semicolon-separated list with no path information, so a server receiving two session values cannot tell from the header alone which path each came from. That ambiguity is one reason to avoid reusing a name at multiple paths.
Partitioned cookies add an implicit component to the key: the top-level site. A cookie set with the Partitioned attribute is filed under (name, domain, path, top-level site) and is only sent when the request’s top-level site matches, which prevents an embedded third party from carrying one identifier across embedding sites. The mechanics are the same as the storage partitioning described in the partition key article, applied to the cookie jar rather than to databases and caches.
Cookies remain the only storage mechanism the browser volunteers to the server without any script involvement, which is precisely why their scope rules are so much older, more permissive, and more easily misread than those of the origin-keyed APIs.
References
- RFC 6265: HTTP State Management Mechanism — IETF
- MDN — Using HTTP cookies — MDN Web Docs
- MDN — Set-Cookie header — MDN Web Docs