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

AttributeControlsIf omittedWatch out for
DomainHosts that receive the cookieHost-only: exact host onlyPublic suffixes are rejected; a leading dot is ignored
PathURL paths that receive the cookieDirectory of the URL that set itNot a security boundary
ExpiresAbsolute expiry date in GMTSession cookieDepends on the client clock
Max-AgeSeconds until expirySession cookieWins 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.

All guides