Куки, document.cookie

Для чтения и записи cookie используется свойство document.cookie. Однако, оно представляет собой не объект, а строку в специальном формате, для удобной манипуляций с которой нужны дополнительные функции.

Чтение document.cookie

Наверняка у вас есть cookie, которые привязаны к этому сайту. Давайте полюбуемся на них. Вот так:

alert( document.cookie );

Эта строка состоит из пар ключ=значение, которые перечисляются через точку с запятой с пробелом "; ".

Значит, чтобы прочитать cookie, достаточно разбить строку по "; ", и затем найти нужный ключ. Это можно делать либо через split и работу с массивом, либо через регулярное выражение.

Функция getCookie(name)

Следующая функция getCookie(name) возвращает cookie с именем name:

// возвращает cookie с именем name, если есть, если нет, то undefined
function getCookie(name) {
  var matches = document.cookie.match(new RegExp(
    "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
  ));
  return matches ? decodeURIComponent(matches[1]) : undefined;
}

Обратим внимание, что значение может быть любым. Если оно содержит символы, нарушающие форматирование, например, пробелы или ;, то оно кодируется при помощи encodeURIComponent. Функция getCookie автоматически раскодирует его.

Запись в document.cookie

В document.cookie можно писать. При этом запись не перезаписывает существующие cookie, а дополняет к ним!

Например, такая строка поставит cookie с именем userName и значением Vasya:

document.cookie = "userName=Vasya";

…Однако, всё не так просто. У cookie есть ряд важных настроек, которые очень желательно указать, так как значения по умолчанию у них неудобны.

Эти настройки указываются после пары ключ=значение, каждое – после точки с запятой:

path=/mypath

Путь, внутри которого будет доступ к cookie. Если не указать, то имеется в виду текущий путь и все пути ниже него.

Как правило, используется path=/, то есть cookie доступно со всех страниц сайта.

domain=site.com

Домен, на котором доступно cookie. Если не указать, то текущий домен. Допустимо указывать текущий домен site.com и его поддомены, например forum.site.com.

Если указать специальную маску .site.com, то cookie будет доступно на сайте и всех его поддоменах. Это используется, например, в случаях, когда кука содержит данные авторизации и должна быть доступна как на site.com, так и на forum.site.com.

expires=Tue, 19 Jan 2038 03:14:07 GMT

Дата истечения куки в формате GMT. Получить нужную дату можно, используя объект Date. Его можно установить в любое время, а потом вызвать toUTCString(), например:

// +1 день от текущего момента
var date = new Date;
date.setDate(date.getDate() + 1);
alert( date.toUTCString() );

Если дату не указать, то cookie будет считаться «сессионным». Такое cookie удаляется при закрытии браузера. Если дата в прошлом, то кука будет удалена.

secure

Cookie можно передавать только по HTTPS.

Например, чтобы поставить cookie name=value по текущему пути с датой истечения через 60 секунд:

var date = new Date(new Date().getTime() + 60 * 1000);
document.cookie = "name=value; path=/; expires=" + date.toUTCString();

Чтобы удалить это cookie:

var date = new Date(0);
document.cookie = "name=; path=/; expires=" + date.toUTCString();

При удалении значение не важно. Можно его не указывать, как сделано в коде выше.

Функция setCookie(name, value, options)

Если собрать все настройки воедино, вот такая функция ставит куки:

function setCookie(name, value, options) {
  options = options || {};

  var expires = options.expires;

  if (typeof expires == "number" && expires) {
    var d = new Date();
    d.setTime(d.getTime() + expires * 1000);
    expires = options.expires = d;
  }
  if (expires && expires.toUTCString) {
    options.expires = expires.toUTCString();
  }

  value = encodeURIComponent(value);

  var updatedCookie = name + "=" + value;

  for (var propName in options) {
    updatedCookie += "; " + propName;
    var propValue = options[propName];
    if (propValue !== true) {
      updatedCookie += "=" + propValue;
    }
  }

  document.cookie = updatedCookie;
}

Аргументы:

name

название cookie

value

значение cookie (строка)

options

Объект с дополнительными свойствами для установки cookie:

expires

Время истечения cookie. Интерпретируется по-разному, в зависимости от типа:

  • Число – количество секунд до истечения. Например, expires: 3600 – кука на час.
  • Объект типа Date – дата истечения.
  • Если expires в прошлом, то cookie будет удалено.
  • Если expires отсутствует или 0, то cookie будет установлено как сессионное и исчезнет при закрытии браузера.
path

Путь для cookie.

domain

Домен для cookie.

secure

Если true, то пересылать cookie только по защищенному соединению.

Функция deleteCookie(name)

Здесь всё просто – удаляем вызовом setCookie с датой в прошлом.

function deleteCookie(name) {
  setCookie(name, "", {
    expires: -1
  })
}

Сторонние cookie

При работе с cookie есть важная тонкость, которая касается внешних ресурсов.

Теоретически, любой ресурс, который загружает браузер, может поставить cookie.

Например:

  • Если на странице есть <img src="http://mail.ru/counter.gif">, то вместе с картинкой в ответ сервер может прислать заголовки, устанавливающие cookie.
  • Если на странице есть <iframe src="http://facebook.com/button.php">, то во-первых сервер может вместе с button.php прислать cookie, а во-вторых JS-код внутри ифрейма может записать в document.cookie

При этом cookie будут принадлежать тому домену, который их поставил. То есть, на mail.ru для первого случая, и на facebook.com во втором.

Такие cookie, которые не принадлежат основной странице, называются «сторонними» (3rd party) cookies. Не все браузеры их разрешают.

Как правило, в настройках браузера можно поставить «Блокировать данные и файлы cookie сторонних сайтов» (Chrome).

В Safari такая настройка включена по умолчанию и выглядит так:

Тс-с-с. Большой брат смотрит за тобой.

Цель этого запрета – защитить посетителей от слежки со стороны рекламодателей, которые вместе с картинкой-баннером присылают и куки, таким образом помечая посетителей.

Например, на многих сайтах стоят баннеры и другая реклама Google Ads. При помощи таких cookie компания Google будет знать, какие именно сайты вы посещаете, сколько времени вы на них проводите и многое другое.

Как? Да очень просто – на каждом сайте загружается, к примеру, картинка с рекламой. При этом баннер берётся с домена, принадлежащего Google. Вместе с баннером Google ставит cookie со специальным уникальным идентификатором.

Далее, при следующем запросе на баннер, браузер пошлёт стандартные заголовки, которые включают в себя:

  • Cookie с домена баннера, то есть уникальный идентификатор, который был поставлен ранее.
  • Стандартный заголовок Referrer (его не будет при HTTPS!), который говорит, с какого сайта сделан запрос. Да, впрочем, Google и так знает, с какого сайта запрос, ведь идентификатор сайта есть в URL.

Так что Google может хранить в своей базе, какие именно сайты из тех, на которых есть баннер Google, вы посещали, когда вы на них были, и т.п. Этот идентификатор легко привязывается к остальной информации от других сервисов, и таким образом картина слежки получается довольно-таки глобальной.

Здесь я не утверждаю, что в конкретной компании Google всё именно так… Но во-первых, сделать так легко, во-вторых идентификаторы действительно ставятся, а в-третьих, такие знания о человеке позволяют решать, какую именно рекламу и когда ему показать. А это основная доля доходов Google, благодаря которой корпорация существует.

Возможно, компания Apple, которая выпустила Safari, поставила такой флаг по умолчанию именно для уменьшения влияния Google?

А если очень надо?

Итак, Safari запрещает сторонние cookie по умолчанию. Другие браузеры предоставляют такую возможность, если посетитель захочет.

А что, если ну очень надо поставить стороннюю cookie, и чтобы это было надёжно?

Такая задача действительно возникает, например, в системе кросс-доменной авторизации, когда есть несколько доменов 2-го уровня, и хочется, чтобы посетитель, который входит в один сайт, автоматически распознавался во всей сетке. При этом cookie для авторизации ставятся на главный домен – «мастер», а остальные сайты запрашивают их при помощи специального скрипта (и, как правило, копируют к себе для оптимизации, но здесь это не суть).

Ещё пример – когда есть внешний виджет, например, iframe с информационным сервисом, который можно подключать на разные сайты. И этот iframe должен знать что-то о посетителе, опять же, авторизация или какие-то настройки, которые хорошо бы хранить в cookie.

Есть несколько способов поставить 3rd-party cookie для Safari.

Использовать ифрейм.

Ифрейм является полноценным окном браузера. В нём должна быть доступна вся функциональность, в том числе cookie. Как браузер решает, что ифрейм «сторонний» и нужно запретить для него и его скриптов установку cookie? Критерий таков: «в ифрейме нет навигации». Если навигация есть, то ифрейм признаётся полноценным окном.

Например, в сторонний iframe можно сделать POST. И тогда, в ответ на POST, сервер может поставить cookie. Или прислать документ, который это делает. Ифрейм, в который прошёл POST, считается родным и надёжным.

Popup-окно

Другой вариант – использовать popup, то есть при помощи window.open открывать именно окно со стороннего домена, и уже там ставить cookie. Это тоже работает.

Редирект

Ещё одно альтернативное решение, которое подходит не везде – это сделать интеграцию со сторонним доменом, такую что на него можно сделать редирект, он ставит cookie и делает редирект обратно.

Дополнительно

  • На Cookie наложены ограничения:

    • Имя и значение (после encodeURIComponent) вместе не должны превышать 4кб.
    • Общее количество cookie на домен ограничено 30-50, в зависимости от браузера.
    • Разные домены 2-го уровня полностью изолированы. Но в пределах доменов 3-го уровня куки можно ставить свободно с указанием domain.
    • Сервер может поставить cookie с дополнительным флагом HttpOnly. Cookie с таким параметром передаётся только в заголовках, оно никак не доступно из JavaScript.
  • Иногда посетители отключают cookie. Отловить это можно проверкой свойства navigator.cookieEnabled

    if (!navigator.cookieEnabled) {
      alert( 'Включите cookie для комфортной работы с этим сайтом' );
    }

    …Конечно, предполагается, что включён JavaScript. Впрочем, посетитель без JS и cookie с большой вероятностью не человек, а бот.

Итого

Файл с функциями для работы с cookie: cookie.js.

Карта учебника

Комментарии

перед тем как писать…
  • Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
  • Для одной строки кода используйте тег <code>, для нескольких строк кода — тег <pre>, если больше 10 строк — ссылку на песочницу (plnkr, JSBin, codepen…)
  • Если что-то непонятно в статье — пишите, что именно и с какого места.