Guide
Domain, Path, Expires and Max-Age Explained
Learn how the Domain, Path, Expires and Max-Age cookie attributes decide where a cookie is sent and how long it lives, with common mistakes to avoid.
Key takeaways
- Domain and Path control where a cookie is sent; Expires and Max-Age control how long it lives.
- Without a Domain attribute a cookie is host-only; with one it also goes to every subdomain.
- Max-Age (seconds from receipt) has precedence over Expires, which is an absolute GMT date that depends on the client clock.
- Path is not a security boundary, and browsers reject Domain values that are public suffixes such as com or co.uk.
- To delete a cookie, repeat its name, domain and path with Max-Age=0.
Attributes shape the scope and the lifetime
A cookie on its own is just a name and a value. The attributes after the first semicolon in a Set-Cookie header answer two questions: where should the browser send this cookie, and for how long should it keep it? Four attributes handle most of that work. Domain and Path control scope. Expires and Max-Age control lifetime. Understanding them well prevents a large share of the bugs that developers meet in real projects.
What does the Domain attribute do?
When a server sets a cookie without a Domain attribute, the browser stores it as a host-only cookie. That means it will be sent back only to the exact host that set it. A cookie set by www.example.com without a Domain attribute is not sent to api.example.com or to example.com. This is the safest default, and for many applications it is exactly what you want.
When you add Domain=example.com, the cookie becomes available to that host and to its subdomains. It will travel to www.example.com, api.example.com and any other subdomain. A leading dot in older documentation is ignored (RFC 6265 and MDN both say so), so Domain=example.com and Domain=.example.com are treated alike. Use the wider scope only when you genuinely need to share the cookie across subdomains, for example a single sign-on across several sections of one product.
There are limits on what a server may claim. A response from one host cannot set a cookie for an unrelated domain, and browsers refuse cookies scoped to a public suffix, such as a bare top-level domain or a shared hosting suffix. The browsers consult a maintained list of public suffixes for this purpose. If your Domain value is rejected silently, mismatch with the response host or a public suffix is a likely reason.
What does the Path attribute do?
Path narrows the cookie to a part of the site. With Path=/admin, the cookie is sent for requests to /admin and below, such as /admin/users, but not to /blog. If Path is omitted, the browser uses a default derived from the path of the request that produced the response, which is usually the directory part of that address. That default surprises many people. A cookie set while visiting /shop/cart/checkout without a Path attribute may be limited to /shop/cart, and then disappear from other pages.
For most session and preference cookies, setting Path=/ explicitly is the clearest choice. Keep in mind that Path is not a security boundary. Pages on the same origin can still read each other's non-HttpOnly cookies through script, regardless of path. Treat Path as a convenience for reducing traffic, not as access control.
How does the Expires attribute work?
Expires takes an absolute date and time in a standard HTTP date format, written in Greenwich Mean Time. An example is Expires=Wed, 21 Oct 2026 07:28:00 GMT. When that moment passes, the browser deletes the cookie. Because the value is an absolute clock time, it depends on the client clock being roughly right. If a visitor's device has the wrong date, a cookie may expire immediately or live far longer than planned.
How does the Max-Age attribute work?
Max-Age takes a number of seconds from the moment the browser receives the header, for example Max-Age=3600 for one hour. Because it is relative, it avoids clock skew problems. If both Expires and Max-Age are present, Max-Age has precedence, as both RFC 6265 and MDN state. Many developers therefore set only Max-Age for new code, and add Expires only when they need to support very old clients.
A value of zero or a negative number tells the browser to remove the cookie straight away. This is the usual way to log someone out: send a Set-Cookie with the same name, domain and path and Max-Age=0.
The four attributes at a glance
| Attribute | Controls | If omitted | Watch out for |
|---|---|---|---|
| Domain | Hosts that receive the cookie | Host-only: exact host only | Public suffixes are rejected; a leading dot is ignored |
| Path | URL paths that receive the cookie | Directory of the URL that set it | Not a security boundary |
| Expires | Absolute expiry date in GMT | Session cookie | Depends on the client clock |
| Max-Age | Seconds until expiry | Session cookie | Wins over Expires; 0 or less deletes |
How long should a cookie live?
Lifetime is a design decision, not just a technical one. A session identifier for a banking style application may be best left as a session cookie, with server-side idle and absolute timeouts. A language preference can live for a year. A cookie that remembers a login across visits deserves extra care, because a long-lived credential in a browser is a long-lived target. Browsers may also cap the maximum lifetime they will honour; some current browsers limit lifetimes to a few hundred days. Because that policy can change, avoid relying on very long lifetimes and design for the cookie to disappear.
- Use session cookies, or short lifetimes, for authentication state.
- Use longer lifetimes only for harmless preferences.
- Enforce expiry on the server as well, so that a stolen or old value stops working even if the browser still holds it.
Common mistakes
- Setting a cookie on a subdomain and expecting the parent domain to receive it.
- Forgetting that a deletion header must repeat the same Domain and Path as the original.
- Leaving out Path and getting an unexpected default scope.
- Assuming Expires is evaluated on the server clock rather than the client clock.
- Using a wide Domain scope on a site that also hosts user-controlled subdomains, which may let one subdomain affect another.
Putting it together
A reasonable header for a login cookie might read Set-Cookie: sid=abc123; Path=/; Max-Age=7200; Secure; HttpOnly; SameSite=Lax, with no Domain attribute so that it stays host-only. That small header already makes four decisions: where it goes, how long it lives, how it travels, and who can touch it. The following guides cover the remaining attributes in the same way.
Sources and further reading
- RFC 6265: HTTP State Management Mechanism: the specification of Set-Cookie and Cookie, including attribute rules, Max-Age precedence over Expires and the minimum limits on cookie size and count.
- MDN: Set-Cookie header: each attribute (Domain, Path, Expires, Max-Age, Secure, HttpOnly, SameSite, Partitioned) and the cookie prefix rules.
- MDN: Using HTTP cookies: session and permanent cookies, deleting a cookie, regenerating session cookies at login, and why cookies suit storage poorly compared with Web Storage.
Frequently asked questions
Should I set the Domain attribute or leave it out?
Leave it out unless you need the cookie on several subdomains. Without Domain the cookie is host-only, which limits exposure and is the safer default.
What happens if I set both Expires and Max-Age?
Current browsers generally prefer Max-Age and ignore Expires. Most new code sets only Max-Age because a relative number of seconds does not depend on the client clock.
Is Path a security feature?
No. Path only limits which requests carry the cookie. Script on another path of the same origin can still read a cookie that is not HttpOnly, so do not rely on Path for protection.
Why can I not set a cookie for a different website's domain?
Browsers only accept a Domain value that matches the host of the response or one of its parent domains, and they refuse public suffixes. This stops one site from planting cookies for another.