# Cookies, document.cookie Cookies are small strings of data that are stored directly in the browser. They are a part of HTTP protocol, defined by [RFC 6265](https://tools.ietf.org/html/rfc6265) specification. Cookies are usually set by a web-server using response `Set-Cookie` HTTP-header. Then the browser automatically adds them to (almost) every request to the same domain using `Cookie` HTTP-header. One of the most widespread use cases is authentication: 1. Upon sign in, the server uses `Set-Cookie` HTTP-header in the response to set a cookie with a unique "session identifier". 2. Next time when the request is set to the same domain, the browser sends the over the net using `Cookie` HTTP-header. 3. 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 ```online Does your browser store any cookies from this site? Let's see: ``` ```offline Assuming you're on a website, it's possible to see the cookies from it, like this: ``` ```js run // 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`: ```js run 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 `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: ```js run // 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 ``` ```warn header="Limitations" There are few limitations: - The `name=value` pair, after `encodeURIComponent`, 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 a 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: ```js run document.cookie = "user=John; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT" ``` ## path - **`path=/mypath`** The url path prefix, the cookie will be accessible for pages under that path. Must be absolute. 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 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 `other.com`. ...But what's more tricky, we also won't get the cookie at a subdomain `forum.site.com`! ```js // 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` get a cookie, that's possible. When setting a cookie at `site.com`, we should explicitly set `domain` option to the root domain: `domain=site.com`: ```js // 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, should be used if we need to support very old browsers. So, `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 browser close, we can set either `expires` or `max-age` option. - **`expires=Tue, 19 Jan 2038 03:14:07 GMT`** Cookie expiration date, when the browser will delete it automatically. The date must be exactly in this format, in GMT timezone. We can use `date.toUTCString` to get it. For instance, we can set the cookie to expire in 1 day: ```js // +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`** An alternative to `expires`, specifies the cookie expiration in seconds from the current moment. If zero or negative, then the cookie is deleted: ```js // cookie will die +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, then the flag is the right thing. ```js // assuming we're on https:// now // set the cookie secure (only accessible if over HTTPS) document.cookie = "user=John; secure"; ``` ## samesite That's another security attribute `samesite`. It's designed to protect from so-called XSRF (cross-site request forgery) attacks. To understand how it works and when it's useful, let's take a look at XSRF attacks. ### XSRF attack Imagine, you are logged into the site `bank.com`. That is: you have an authentication cookie from that site. Your browser sends it to `bank.com` with every request, so that it recognizes you and performs all sensitive financial operations. Now, while browsing the web in another window, you accidentally come to another site `evil.com`. That site has JavaScript code that submits a form `