Even though I’ve been doing web things for a while now, I confess I had never dealt with browser cookies other than clicking those cookie notifications on every other website you visit these days.
I mean, I knew that it was a form of storage on the browser, but I’d always used localStorage
for that. Recently I was working on something that used browser cookies and I figured it was a good time to figure them out.
Why cookie and not some other tasty snack?
I love the name cookie, but I can’t help but wonder if there was a reason for it. Turns out I’m not the only person who had that question. And the inventor of browser cookies, Lou Montulli explained that he had heard the term ‘magic cookie‘ from an operating systems course in college that had a similar meaning to the way his proposed solution for a session identifier worked.
The original problem he was trying to solve was the implementation of an online shopping cart, which eventually led to the original specification for persistent client state, and has since evolved into the current RFC 6265. The first cookies were used to verify repeat visitors to the Netscape website.
So how does this non-edible cookie work?
A cookie is a small plain text file stored in the browser. There isn’t anything executable in there. It simply contains a small amount of data. Every browser stores them in a slightly different location (e.g. Where cookies are located in Windows 10, for all web browsers).
The data in the cookie is sent over by the server, stored on the user’s browsers, then used in subsequent requests as an identifier of sorts. Cookies are mainly used to remember state (if you are logged in, shopping cart items, user preferences etc.) as well as tracking.
Cookies are created when the server sends over one or more Set-Cookie
headers with its response, something along these lines:
Set-Cookie: NAME=VALUE
It could be any name-value pair, but each cookie can contain only 1 name-value pair. If you need more than 1 cookie, then multiple Set-Cookie
headers are needed. An example of a server sending over cookie headers to the browser looks something like this:
HTTP/2.0 200 OK
Content-Type: text/html
Set-Cookie: viola=red_panda
Set-Cookie: mathia=polar_bear
As a frontend developer, I must admit I don’t debug server-sent headers very often so this is not something I see on a regular basis. Once the cookie is set, all subsequent requests to the server from the browser will also have the cookies in its request header.
GET /demos/cookie/ HTTP/2
Host: huijing.github.io
Cookie: viola=red_panda; mathia=polar_bear
Even though cookies are usually created on the server, you can also create them on the client-side with Javascript, using document.cookie
. Browser cookies also have a number of attributes in addition to the name-value pair mentioned earlier.
Cookie attributes
The cookie name can be any US-ASCII characters except control characters, spaces, tabs or separator characters. The cookie value can be optionally wrapped in double quotes and be any US-ASCII characters except control characters, double quotes, commas, semicolons, backslash and whitespace.
Adding special prefixes to the cookie name also forces certain requirements. If your cookie name starts with __Secure-
, it must be set with the secure
flag from a page served with HTTPS
. If your cookie name starts with __Host-
, it must be set with the secure
flag from a page served with HTTPS
, it must not have a domain specified and its path must be /
.
The rest of the attributes are optional but can impact cookie behaviour significantly depending on what values are set.
Max-Age
takes precedence over Expires
if both are set.document.cookie
(to mitigate XSS attacks)Strict
means the cookie is only sent for requests originating from the same URL as the current one.Lax
means the cookie is not sent on cross-site requests, but will be sent if the user navigates to the origin site from an external site.None
means the cookie will be sent on both same-site and cross-site requests, but can only be used if theSecure
attribute is also set.
If you use Firefox, you may notice a console log message like this on some websites.
Back in August 2020, Mozilla made the decision to treat cookies as SameSite=Lax
by default, and require cookies with SameSite=None
to also set the Secure
attribute. The original behaviour for cookies was SameSite=None
but this leaves users susceptible to Cross-Site Request Forgery attacks.
Both Chrome and Firefox has rolled this out, but it seems like only Firefox displays the console log warning? If you can verify the console logging situation, please let me know.
Using cookies to do stuff
Cookies without an Expires
or Max-Age
attribute are treated as session cookies, which means they are removed once the browser is closed. Setting a value on either Expires
or Max-Age
makes them permanent cookies, since they will exist until they hit their expiry date.
Again, I usually don’t do server-side stuff so I’ll only talk about messing around with cookies on the client-side. The Document
has a cookie
property that lets us read and write browser cookies via Javascript.
To see all cookies associated with the document, use document.cookie
. You can type this in the browser’s console and see something like this:
To create a new cookie, you can do something like this:
document.cookie = "xiaohua=tortoise"
If you need more than one cookie, you’ll have to do this for every cookie you want to create.
Even if you refresh the page, the cookie should still be there. To get rid of the cookie, or reset it, you can set the Expires
value to the beginning of time itself, Thu, 01 Jan 1970 00:00:00 GMT
. I’m semi-kidding. Just in case you never heard of this interesting (and fairly important) piece of trivia, I shall quote MDN:
A JavaScript date is fundamentally specified as the number of milliseconds that have elapsed since midnight on January 1, 1970, UTC. This date and time are not the same as the UNIX epoch (the number of seconds that have elapsed since midnight on January 1, 1970, UTC), which is the predominant base value for computer-recorded date and time values.
For example, if I wanted to get rid of the taria
cookie, I would do this:
document.cookie = "taria= ;expires=Thu, 01 Jan 1970 00:00:00 GMT"
Because cookies are strings, doing things based on cookie data involves mostly string manipulation. So I won’t go into that in detail, but here’s a ridiculous demo you can play around with, ideally with DevTools open. It just randomly assigns a group cookie, then shows you something different based on that.
Update: Thomas Steiner shared about the Cookie Store API and its polyfill which lets us avoid having to the do not fun string manipulations mentioned above.
Wrapping up
It’s been a while since I last published anything. I suppose this is the longest hiatus I’ve had since I started this blog, but somehow being stuck in the same place doesn’t seem to motivate me to write words. But we’ll see.
Meanwhile, go eat some of your favourite cookies.
Resource links
- Cookies, Chaos and the Browser: Meet Lou Montulli
- Using HTTP cookies
- Set-Cookie on MDN
- Document.cookie on MDN
Credits: OG:image from Red Panda Loves Cookies video on Oregon Zoo Youtube channel