Cookies are a fundamental part of the Web, as they allow sessions and in general to recognize the users during the navigation.
By using Cookies we can exchange information between the server and the browser to provide a way to customize a user session, and for servers to recognize the user between requests.
HTTP is stateless, which means all request origins to a server are exactly the same and a server cannot determine if a request comes from a client that already did a request before, or it’s a new one.
Cookies are sent by the browser to the server when an HTTP request starts, and they are sent back from the server, which can edit their content.
Cookies are essentially used to store a session id.
In the past cookies were used to store various types of data, since there was no alternative. But nowadays with the Web Storage API (Local Storage and Session Storage) and IndexedDB, we have much better alternatives.
Especially because cookies have a very low limit in the data they can hold, since they are sent back-and-forth for every HTTP request to our server - including requests for assets like images or CSS / JavaScript files.
Cookies have a long history, they had their first version in 1994, and over time they were standardized in multiple RFC revisions.
RFC stands for Request for Comments, the way standards are defined by the Internet Engineering Task Force (IETF), the entity responsible for setting standards for the Internet
The latest specification for Cookies is defined in the RFC 6265, which is dated 2011.
Restrictions of cookies
- Cookies can only store 4KB of data
- Cookies are private to the domain. A site can only read the cookies it set, not other domains cookies
- You can have up to 20 limits of cookies per domain (but the exact number depends on the specific browser implementation)
- Cookies are limited in their total number (but the exact number depends on the specific browser implementation). If this number is exceeded, new cookies replace the older ones.
Cookies can be set or read server side, or client side.
In the client side, cookies are exposed by the document object as document.cookie
Set cookies
The simplest example to set a cookie is:
document.cookie = 'name=Flavio'
This will add a new cookie to the existing ones (it does not overwrite existing cookies)
The cookie value should be url encoded with encodeURIComponent()
, to make sure it does not contain any whitespace, comma or semicolon which are not valid in cookie values.
Set a cookie expiration date
If you don’t set anything else, the cookie will expire when the browser is closed. To prevent so, add an expiration date, expressed in the UTC format (Mon, 27 May 2019 17:04:05 UTC
)
document.cookie = 'name=Flavio; expires=Mon, 27 May 2019 17:04:05 UTC'
A simple JavaScript snippet to set a cookie that expires in 24 hours is:
const date = new Date()
date.setHours(date.getHours() + 5)
document.cookie = 'name=Flavio; expires=' + date.toUTCString()
Alternatively you can use the max-age
parameter to set an expiration expressed in number of seconds:
document.cookie = 'name=Flavio; max-age=3600' //expires in 60 minutes
document.cookie = 'name=Flavio; max-age=31536000' //expires in 1 year
Set a cookie path
The path
parameter specifies a document location for the cookie, so it’s assigned to a specific path, and sent to the server only if the path matches the current document location, or a parent:
document.cookie = 'name=Flavio; path="/dashboard"'
this cookie is sent on /dashboard
, /dashboard/today
and other sub-urls of /dashboard/
, but not on /posts
for example.
If you don’t set a path, it defaults to the current document location. This means that to apply a global cookie from an inner page, you need to specify path="/"
.
Set a cookie domain
The domain
can be used to specify a subdomain for your cookie.
document.cookie = 'name=Flavio; domain="flaviocopes.com";'
If not set, it defaults to the host portion even if using a subdomain (if on subdomain.mydomain.com, by default it’s set to mydomain.com). Domain cookies are included in subdomains.
Cookie Security
Secure
Adding the Secure
parameter makes sure the cookie can only be transmitted securely over HTTPS, and it will not be sent over unencrypted HTTP connections:
document.cookie = 'name=Flavio; Secure;'
Note that this does not make cookies secure in any way - always avoid adding sensitive information to cookies
HttpOnly
One useful parameter is HttpOnly
, which makes cookies inaccessible via the document.cookie
API, so they are only editable by the server:
document.cookie = 'name=Flavio; Secure; HttpOnly'
SameSite
SameSite
, still experimental and only supported by Chrome and Firefox (https://caniuse.com/#feat=same-site-cookie-attribute, lets servers require that a cookie is not sent on cross-site requests, but only on resources that have the cookie domain as the origin, which should be a great help towards reducing the risk of CSRF (Cross Site Request Forgery) attacks.
Update a cookie value or parameter
To update the value of a cookie, just assign a new value to the cookie name:
document.cookie = 'name=Roger'
Similar to updating the value, to update the expiration date, reassign the value with a new expires
or max-age
property:
document.cookie = 'name=Roger; max-age=31536000' //expires in 1 year
Just remember to also add any additional parameters you added in the first place, like path
or domain
.
Delete a cookie
To delete a cookie, unset its value and pass a date in the past:
document.cookie = 'name=; expires=Thu, 01 Jan 1970 00:00:00 UTC;'
(and again, with all the parameters you used to set it)
Access the cookies values
To access a cookie, lookup document.cookie
:
const cookies = document.cookie
This will return a string with all the cookies set for the page, semicolon separated:
'name=Flavio; age=36; car=Ford'
Check if a cookie exists
const checkCookieExists = (name) => {
if (document.cookie.split(';').filter(item => {
if (item.includes(name + '=')) return true
}).length) { return true } else { return false }
}
checkCookieExists('my-cookie-name')
Get a cookie value
This function will return the value of a cookie, if present:
const getCookieValue = name => {
const theCookie = document.cookie.split(';').filter(item => {
if (item.includes(name + '=')) return true
})
if (theCookie.length === 0) { return false }
return theCookie[0].split('=')[1]
}
getCookieValue('my-cookie-name')