Inspect the value of a cookie in JavaScript
Tested on |
Firefox (3.0, 3.6, 31) |
Chrome (5, 34, 36) |
IE (6, 7, 8, 11) |
Objective
To inspect the value of a cookie from within a web page using JavaScript
Scenario
Suppose you wish to read the value of a cookie named foo
.
You are expecting there to be at most one cookie with this name (it having been created with the Path
attribute set to /
and the Domain
attribute left unset), and for its value to be URI-encoded.
Method
Overview
The approach taken here is to extract all of the visible cookies into a hash, which can then be queried for the specific cookie you want to inspect. There is some overhead in doing this, which may or may not be worthwhile depending on how many cookies there are and how many you want access to. An alternative method which performs a direct search is described later.
There are five steps:
- Read the list of cookies from
document.cookie
. - Split the list into individual cookies.
- Split each cookie into a name and a value.
- Index the cookies by name.
- Fetch and decode the value of the required cookie.
Read the list of cookies from document.cookie
From JavaScript, cookies can be set or inspected using the cookie
property of the global document
object. When read this returns a semicolon-separated list of all cookies that are associated with the current document:
document.cookie
(Be aware that this does not behave like an ordinary variable: strings read from document.cookie
are not in an appropriate format for writing back to it.)
Split the list into individual cookies
The string containing the cookie list can then be converted into an array of individual cookies using the split
method, with a semicolon followed by a space as the separator string (U+003B, U+0020). An empty string should be treated as a special case: left it its own devices split
would return a single-element array containing an empty string, whereas the desired output is an empty array (to indicate that there are no cookies):
var cookieList = (document.cookie) ? document.cookie.split('; ') : [];
You may encounter code which uses a regular expression to split the cookie string, but there should be no need to do this: RFC 6265 promises a semicolon followed by exactly one space, and there are enough websites which depend on this behaviour to make conformance a safe assumption.
Split each cookie into a name and a value
The name and value of each cookie are separated by an equals sign (U+003D), however it is also permissible for this character to appear in the value. Therefore, to find the separator it is necessary to search forwards from the start of the string for the first instance of an equals sign. This can be done using the indexOf
method:
for (var i = 0, n = cookieList.length; i != n; ++i) { var cookie = cookieList[i]; var f = cookie.indexOf('='); if (f >= 0) { var cookieName = cookie.substring(0, f); var cookieValue = cookie.substring(f + 1); // [...] } }
Note that document.cookie
does not provide access to cookie attributes: only the name and value are provided.
The value of the cookie may need to be decoded, but it is usually best to do this after isolating a specific cookie of interest. Decoding at this stage would commit you to using the same coding method for all cookies, which might not be appropriate.
Index the cookies by name
In the scenario described above, the cookies visible to any given web page are expected to have distinct names. It should therefore be reasonably safe to index them using a data structure which maps cookie names to single cookies. A JavaScript object can be used for this purpose.
As a precaution in case the cookie names are not distinct, it is best to accept the first cookie with a given name and disregard any subsequent ones. This is because most web browsers follow the RFC 6265 recommendation to return cookies with longer path attributes first, and cookies with longer paths are more likely to be relevant to the web pages from which they are visible.
var cookieValues = {}; for (var i = 0, n = cookieList.length; i != n; ++i) { var cookie = cookieList[i]; var f = cookie.indexOf('='); if (f >= 0) { var cookieName = cookie.substring(0, f); var cookieValue = cookie.substring(f + 1); if (!cookieValues.hasOwnProperty(cookieName)) { cookieValues[cookieName] = cookieValue; } } }
Note the use of hasOwnProperty
in preference to in
. This is standard practice when using a JavaScript object as an associative array in order to disregard properties inherited from Object
(which could conceivably clash with one of the names that you want to use).
Fetch and decode the value of the required cookie
Cookies values are often encoded in order to ensure that they do not contain any forbidden characters. In this instance it is URI-encoded, therefore it can be decoded using the decodeURIComponent
function which is built into JavaScript:
if (cookieValues.hasOwnProperty('foo')) { var rawValue = cookieValues['foo']; try { var decodedValue = decodeURIComponent(rawValue); alert('decoded value = "' + decodedValue + '"'); } catch (ex) { alert('failed to decode cookie'); } } else { alert('cookie not found'); }
As above, the hasOwnProperty
method is used to detect whether the cookie is missing. You should try to handle this condition as gracefully as possible, since it can happen inadvertantly for a number of reasons:
- The cookie might have expired due to passage of time.
- The user might have navigated directly to the page where the cookie is used without first visiting the page where it is set.
- The user might have logged out of the web site in one browser tab, then attempted to continue using it in a second tab.
- The web browser might be configured not to accept cookies.
Sample code
var cookieList = (document.cookie) ? document.cookie.split('; ') : []; var cookieValues = {}; for (var i = 0, n = cookieList.length; i != n; ++i) { var cookie = cookieList[i]; var f = cookie.indexOf('='); if (f >= 0) { var cookieName = cookie.substring(0, f); var cookieValue = cookie.substring(f + 1); if (!cookieValues.hasOwnProperty(cookieName)) { cookieValues[cookieName] = cookieValue; } } } if (cookieValues.hasOwnProperty('foo')) { var rawValue = cookieValues['foo']; try { var decodedValue = decodeURIComponent(rawValue); alert('decoded value = "' + decodedValue + '"'); } catch (ex) { alert('failed to decode cookie'); } } else { alert('cookie not found'); }
Alternatives
Directly inspecting a single cookie
The method described above extracts all of the cookies that are visible to the web page, which could be inefficient if only one is needed. An alternative is to search directly for the cookie of interest. This can be done using a regular expression:
var regexp = new RegExp('(?:^|; )' + cookieName + '=([^;]*)(?:$|; )'); var match = document.cookie.match(regexp); if (match) { var rawValue = match[1]; try { var decodedValue = decodeURIComponent(rawValue); alert('decoded value = "' + decodedValue + '"'); } catch (ex) { alert('failed to decode cookie'); } } else { alert('cookie not found'); }
The regular expression regexp
matches the following sequence:
- either the start of the string, or a semicolon followed by a space;
- the required cookie name;
- an equals sign;
- the cookie value (which is captured); and
- either a semicolon followed by a space, or the end of the string.
Note
Handling multiple cookies with the same name
If you attempt to set two cookies with the same name, path and domain then the second will overwrite the first, whereas if the path or domain are different then the cookies will coexist. This is rarely a desired outcome, however there several ways in which it can arise inadvertantly:
- If you set cookies without specifying an explicit path, then pages in a subdirectory will see both their own cookies and those from higher levels in the directory hierarchy.
- If you have an explicit path, but this changes due to a website reorganisation, there may be a period during which users have both old and new cookies set.
- If two parts of a site are administered independently then the same cookie name could be used by chance.
RFC 6265 recommends that web browsers should list cookies with longer paths before cookies with shorter paths. On the face of it this is a useful heuristic: if a website chooses the first listed cookie with a matching name then that will probably result in the desired behaviour, whether or not an explicit path attribute was specified. However, there is a catch. Ordering by path length is merely a recommendation and not a requirement. For this reason RFC 6265 further recommends that web servers should not depend on the order in which cookies are listed.
This uncertainty makes it very difficult to safely use multiple cookies with the same name, and the author’s advice is not to try. Steps you can take to prevent this from happening unintentionally include the following:
- Always specify an explicit, fixed
Path
attribute when setting cookies (typically the root of the domain). - If the path should need to change for any reason then choose a new cookie name to avoid collisions during the transition.
- If multiple cookies are needed with overlapping scope then distinguish them by name rather than path.
If you do this then it is reasonable to pick the first listed cookie with a matching name: there should only be one, therefore you are not relying on the order for the normal operation of the website. Alternatively, if the cookie is capable of being validated in some way, a more defensive approach would be to accept the first valid cookie with a matching name.
See Also
Further Reading
- Document.cookie, Web API Interfaces, Mozilla Developer Network
- A. Barth, HTTP State Management Mechanism, RFC 6265, IETF, April 2011
- Persistent Client State HTTP Cookies, Netscape (historical specification)
- Cookies, QuirksMode
(Note that RFC 6265 and the Netscape specification technically refer to accessing cookies via HTTP and not from JavaScript. In practice you can overlook this distinction for most purposes, and must do if you want more detail than the very limited amount of documentation available for the JavaScript API.)
Tags: cookie | javascript