Cookies are small strings of data that are stored directly in the browser. They are a part of the HTTP protocol, defined by the RFC 6265 specification.
Cookies are usually set by a web-server using the response Set-Cookie HTTP-header. Then, the browser automatically adds them to (almost) every request to the same domain using the Cookie HTTP-header.
One of the most widespread use cases is authentication:
- Upon sign in, the server uses the
Set-CookieHTTP-header in the response to set a cookie with a unique “session identifier”. - Next time when the request is sent to the same domain, the browser sends the cookie over the net using the
CookieHTTP-header. - So the server knows who made the request.
We can also access cookies from the browser, using document.cookie property.
There are many tricky things about cookies and their options. In this chapter we’ll cover them in detail.
Reading from document.cookie
Does your browser store any cookies from this site? Let’s see:
// At javascript.info, we use Google Analytics for statistics,
// so there should be some cookies
alert( document.cookie ); // cookie1=value1; cookie2=value2;...
The value of document.cookie consists of name=value pairs, delimited by ;. Each one is a separate cookie.
To find a particular cookie, we can split document.cookie by ;, and then find the right name. We can use either a regular expression or array functions to do that.
We leave it as an exercise for the reader. Also, at the end of the chapter you’ll find helper functions to manipulate cookies.
Writing to document.cookie
We can write to document.cookie. But it’s not a data property, it’s an accessor (getter/setter). An assignment to it is treated specially.
A write operation to document.cookie updates only cookies mentioned in it, but doesn’t touch other cookies.
For instance, this call sets a cookie with the name user and value John:
document.cookie = "user=John"; // update only cookie named 'user'
alert(document.cookie); // show all cookies
If you run it, then probably you’ll see multiple cookies. That’s because the document.cookie= operation does not overwrite all cookies. It only sets the mentioned cookie user.
Technically, name and value can have any characters. To keep the valid formatting, they should be escaped using a built-in encodeURIComponent function:
// special characters (spaces), need encoding
let name = "my name";
let value = "John Smith"
// encodes the cookie as my%20name=John%20Smith
document.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value);
alert(document.cookie); // ...; my%20name=John%20Smith
There are few limitations:
- The
name=valuepair, afterencodeURIComponent, should not exceed 4KB. So we can’t store anything huge in a cookie. - The total number of cookies per domain is limited to around 20+, the exact limit depends on the browser.
Cookies have several options, many of them are important and should be set.
The options are listed after key=value, delimited by ;, like this:
document.cookie = "user=John; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT"
path
path=/mypath
The url path prefix must be absolute. It makes the cookie accessible for pages under that path. By default, it’s the current path.
If a cookie is set with path=/admin, it’s visible at pages /admin and /admin/something, but not at /home or /adminpage.
Usually, we should set path to the root: path=/ to make the cookie accessible from all website pages.
domain
domain=site.com
A domain defines where the cookie is accessible. In practice though, there are limitations. We can’t set any domain.
By default, a cookie is accessible only at the domain that set it. So, if the cookie was set by site.com, we won’t get it at other.com.
…But what’s more tricky, we also won’t get the cookie at a subdomain forum.site.com!
// at site.com
document.cookie = "user=John"
// at forum.site.com
alert(document.cookie); // no user
There’s no way to let a cookie be accessible from another 2nd-level domain, so other.com will never receive a cookie set at site.com.
It’s a safety restriction, to allow us to store sensitive data in cookies, that should be available only on one site.
…But if we’d like to allow subdomains like forum.site.com to get a cookie, that’s possible. When setting a cookie at site.com, we should explicitly set the domain option to the root domain: domain=site.com:
// at site.com
// make the cookie accessible on any subdomain *.site.com:
document.cookie = "user=John; domain=site.com"
// later
// at forum.site.com
alert(document.cookie); // has cookie user=John
For historical reasons, domain=.site.com (a dot before site.com) also works the same way, allowing access to the cookie from subdomains. That’s an old notation and should be used if we need to support very old browsers.
So, the domain option allows to make a cookie accessible at subdomains.
expires, max-age
By default, if a cookie doesn’t have one of these options, it disappears when the browser is closed. Such cookies are called “session cookies”
To let cookies survive a browser close, we can set either the expires or max-age option.
expires=Tue, 19 Jan 2038 03:14:07 GMT
The cookie expiration date defines the time, when the browser will automatically delete it.
The date must be exactly in this format, in the GMT timezone. We can use date.toUTCString to get it. For instance, we can set the cookie to expire in 1 day:
// +1 day from now
let date = new Date(Date.now() + 86400e3);
date = date.toUTCString();
document.cookie = "user=John; expires=" + date;
If we set expires to a date in the past, the cookie is deleted.
max-age=3600
Is an alternative to expires and specifies the cookie’s expiration in seconds from the current moment.
If set to zero or a negative value, the cookie is deleted:
// cookie will die in +1 hour from now
document.cookie = "user=John; max-age=3600";
// delete cookie (let it expire right now)
document.cookie = "user=John; max-age=0";
secure
secure
The cookie should be transferred only over HTTPS.
By default, if we set a cookie at http://site.com, then it also appears at https://site.com and vice versa.
That is, cookies are domain-based, they do not distinguish between the protocols.
With this option, if a cookie is set by https://site.com, then it doesn’t appear when the same site is accessed by HTTP, as http://site.com. So if a cookie has sensitive content that should never be sent over unencrypted HTTP, the secure flag is the right thing.
// assuming we're on https:// now
// set the cookie to be secure (only accessible over HTTPS)
document.cookie = "user=John; secure";