Rate this page

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:

  1. Read the list of cookies from document.cookie.
  2. Split the list into individual cookies.
  3. Split each cookie into a name and a value.
  4. Index the cookies by name.
  5. 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:

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:

  1. either the start of the string, or a semicolon followed by a space;
  2. the required cookie name;
  3. an equals sign;
  4. the cookie value (which is captured); and
  5. 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:

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:

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

(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