← JavaScript/Куки: document.cookie#133 из 383← ПредыдущийСледующий →+20 XP
Полезно по теме:Гайд: как учить JavaScriptПрактика: JS базаПрактика: async и сетьТермин: Closure

Куки: document.cookie

Пользователь выбирает язык интерфейса — русский. Через неделю он снова открывает сайт: язык должен сохраниться. Простое решение — куки: небольшие данные, которые браузер хранит и автоматически отправляет на сервер с каждым HTTP-запросом. В отличие от localStorage — сервер их тоже видит.

Что решают куки

Куки решают две задачи: сессия (сервер знает, кто авторизован) и настройки (язык, тема, регион). Главное отличие от localStorage — куки уходят на сервер с каждым запросом.

На основе предыдущих уроков

  • строки: парсинг строки куки через split и indexOf
  • числа: max-age в секундах
  • объекты: Object.keys/entries при обходе кук
  • Чтение и запись

    document.cookie ведёт себя необычно: при чтении возвращает все куки, при записи — добавляет одну:

    // Чтение — возвращает ВСЕ куки через "; "
    console.log(document.cookie)
    // "session_id=abc123; lang=ru; theme=dark"
    
    // Запись — добавляет ОДНУ куку (не перезаписывает остальные!)
    document.cookie = "username=ivan_petrov"
    document.cookie = "lang=ru"
    // Теперь обе куки существуют

    Атрибуты кук

    // expires — дата истечения (UTC строка)
    document.cookie = "session=abc; expires=Fri, 31 Dec 2025 23:59:59 GMT"
    
    // max-age — время жизни в секундах (предпочтительнее expires)
    document.cookie = "token=xyz; max-age=86400"   // 1 день
    document.cookie = "prefs=dark; max-age=2592000" // 30 дней
    
    // path — куки видна только по этому пути и ниже
    document.cookie = "admin=true; path=/admin"
    
    // domain — для какого домена (с поддоменами: .example.com)
    document.cookie = "uid=123; domain=.myshop.ru"
    
    // secure — отправлять только по HTTPS
    document.cookie = "token=abc; secure"
    
    // samesite — защита от CSRF
    document.cookie = "csrf=xyz; samesite=strict"
    // strict — только запросы с того же сайта
    // lax — разрешает GET при переходе по ссылке (по умолчанию)
    // none — всегда (требует secure)

    HttpOnly — недоступны из JavaScript

    Куки с флагом HttpOnly нельзя прочитать через document.cookie. Они устанавливаются сервером и существуют только в HTTP-запросах:

    Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Strict

    Это защищает от XSS: даже если злоумышленник выполнит JS, он не сможет украсть такую куку.

    Вспомогательные функции

    function setCookie(name, value, options = {}) {
      let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`
    
      if (options.maxAge) cookie += `; max-age=${options.maxAge}`
      if (options.expires) cookie += `; expires=${options.expires.toUTCString()}`
      if (options.path) cookie += `; path=${options.path}`
      if (options.domain) cookie += `; domain=${options.domain}`
      if (options.secure) cookie += '; secure'
      if (options.sameSite) cookie += `; samesite=${options.sameSite}`
      if (options.httpOnly) cookie += '; httponly'
    
      document.cookie = cookie
    }
    
    function getCookie(name) {
      const cookies = parseCookies(document.cookie)
      return cookies[name] ?? null
    }
    
    function deleteCookie(name, path = '/') {
      // Устанавливаем прошедшую дату — браузер удаляет куку
      document.cookie = `${encodeURIComponent(name)}=; max-age=0; path=${path}`
    }
    
    function parseCookies(cookieString) {
      if (!cookieString) return {}
      return cookieString
        .split('; ')
        .reduce((acc, pair) => {
          const idx = pair.indexOf('=')
          const key = decodeURIComponent(pair.slice(0, idx).trim())
          const val = decodeURIComponent(pair.slice(idx + 1))
          acc[key] = val
          return acc
        }, {})
    }

    Реальный пример: настройки пользователя

    // Сохранить язык интерфейса на 1 год
    setCookie('lang', 'ru', {
      maxAge: 365 * 24 * 60 * 60,
      path: '/',
      sameSite: 'lax',
    })
    
    // Прочитать при загрузке страницы
    const lang = getCookie('lang') || 'ru'
    applyLanguage(lang)
    
    // Удалить при выходе из аккаунта
    function logout() {
      deleteCookie('session_id')
      deleteCookie('csrf_token')
      window.location.href = '/login'
    }

    Типичные ошибки

    Ошибка 1: хранят чувствительные данные в куке без HttpOnly

    // Сломано: доступно через XSS-атаку
    document.cookie = 'session_token=abc123'
    
    // Исправлено: сессию устанавливает сервер с HttpOnly
    // Set-Cookie: session_token=abc123; HttpOnly; Secure; SameSite=Strict
    // HttpOnly — JS вообще не может прочитать эту куку

    Ошибка 2: не указывают path — кука видна не на всех страницах

    // Сломано: кука доступна только на /profile и ниже
    document.cookie = 'lang=ru'  // path по умолчанию — текущий путь
    
    // Исправлено: path=/ — кука видна везде
    document.cookie = 'lang=ru; path=/'

    Ошибка 3: парсят куки через split('=') — ломается при значениях с '='

    // Сломано: JWT-токен содержит '=' в base64
    // 'token=eyJ...abc=' → split('=') даст ['token', 'eyJ...abc', '']
    
    // Исправлено: используем indexOf('=') для нахождения первого '='
    const idx = pair.indexOf('=')
    const key = pair.slice(0, idx).trim()
    const val = decodeURIComponent(pair.slice(idx + 1))  // всё после первого '='

    Куки vs localStorage

    | | Куки | localStorage |

    |---|---|---|

    | Отправляются на сервер | Да, автоматически | Нет |

    | Размер | ~4 KB | 5-10 MB |

    | Срок жизни | Настраивается | До явного удаления |

    | HttpOnly | Да (недоступны из JS) | Нет |

    | Доступность | Браузер + сервер | Только браузер |

    В реальных проектах

  • Сессии: cookie с HttpOnly от сервера — стандарт аутентификации (Next-Auth, Passport.js)
  • Настройки: язык, тема, регион — хранятся в куках чтобы сервер мог рендерить правильную версию
  • A/B тесты: id варианта хранится в куке — сервер отдаёт нужную версию
  • GDPR: баннер с куками — обязательно для EU пользователей; consent-куки хранят согласие
  • Примеры

    Реализация вспомогательных функций getCookie, setCookie, deleteCookie

    // Вспомогательные функции для работы с куками
    
    function parseCookies(cookieString) {
      if (!cookieString.trim()) return {}
      return cookieString
        .split('; ')
        .reduce((acc, pair) => {
          const idx = pair.indexOf('=')
          if (idx === -1) return acc
          const key = decodeURIComponent(pair.slice(0, idx).trim())
          const val = decodeURIComponent(pair.slice(idx + 1))
          acc[key] = val
          return acc
        }, {})
    }
    
    function setCookie(name, value, options = {}) {
      let cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value)
    
      if (options.maxAge !== undefined) cookie += '; max-age=' + options.maxAge
      if (options.path) cookie += '; path=' + options.path
      if (options.domain) cookie += '; domain=' + options.domain
      if (options.secure) cookie += '; secure'
      if (options.sameSite) cookie += '; samesite=' + options.sameSite
    
      // В браузере: document.cookie = cookie
      return cookie  // возвращаем для демонстрации
    }
    
    function getCookie(cookieString, name) {
      const cookies = parseCookies(cookieString)
      return cookies[name] ?? null
    }
    
    function deleteCookie(name, path = '/') {
      return encodeURIComponent(name) + '=; max-age=0; path=' + path
    }
    
    // Демонстрация
    const cookieStr = 'session_id=abc123XYZ; lang=ru; theme=dark; user_id=42'
    
    const cookies = parseCookies(cookieStr)
    console.log('Все куки:', cookies)
    // { session_id: 'abc123XYZ', lang: 'ru', theme: 'dark', user_id: '42' }
    
    console.log('lang:', getCookie(cookieStr, 'lang'))    // 'ru'
    console.log('theme:', getCookie(cookieStr, 'theme'))  // 'dark'
    console.log('missing:', getCookie(cookieStr, 'role')) // null
    
    // setCookie — возвращает строку для document.cookie
    const newCookie = setCookie('lang', 'en', {
      maxAge: 365 * 24 * 60 * 60,
      path: '/',
      sameSite: 'lax',
    })
    console.log('\nСтрока для установки куки:')
    console.log(newCookie)
    // 'lang=en; max-age=31536000; path=/; samesite=lax'
    
    const deletionStr = deleteCookie('session_id')
    console.log('\nСтрока для удаления куки:')
    console.log(deletionStr)
    // 'session_id=; max-age=0; path=/'
    
    // Пример реального использования: язык интерфейса
    const userLang = getCookie(cookieStr, 'lang') || 'ru'
    console.log('\nЯзык интерфейса:', userLang)  // 'ru'

    Куки: document.cookie

    Пользователь выбирает язык интерфейса — русский. Через неделю он снова открывает сайт: язык должен сохраниться. Простое решение — куки: небольшие данные, которые браузер хранит и автоматически отправляет на сервер с каждым HTTP-запросом. В отличие от localStorage — сервер их тоже видит.

    Что решают куки

    Куки решают две задачи: сессия (сервер знает, кто авторизован) и настройки (язык, тема, регион). Главное отличие от localStorage — куки уходят на сервер с каждым запросом.

    На основе предыдущих уроков

  • строки: парсинг строки куки через split и indexOf
  • числа: max-age в секундах
  • объекты: Object.keys/entries при обходе кук
  • Чтение и запись

    document.cookie ведёт себя необычно: при чтении возвращает все куки, при записи — добавляет одну:

    // Чтение — возвращает ВСЕ куки через "; "
    console.log(document.cookie)
    // "session_id=abc123; lang=ru; theme=dark"
    
    // Запись — добавляет ОДНУ куку (не перезаписывает остальные!)
    document.cookie = "username=ivan_petrov"
    document.cookie = "lang=ru"
    // Теперь обе куки существуют

    Атрибуты кук

    // expires — дата истечения (UTC строка)
    document.cookie = "session=abc; expires=Fri, 31 Dec 2025 23:59:59 GMT"
    
    // max-age — время жизни в секундах (предпочтительнее expires)
    document.cookie = "token=xyz; max-age=86400"   // 1 день
    document.cookie = "prefs=dark; max-age=2592000" // 30 дней
    
    // path — куки видна только по этому пути и ниже
    document.cookie = "admin=true; path=/admin"
    
    // domain — для какого домена (с поддоменами: .example.com)
    document.cookie = "uid=123; domain=.myshop.ru"
    
    // secure — отправлять только по HTTPS
    document.cookie = "token=abc; secure"
    
    // samesite — защита от CSRF
    document.cookie = "csrf=xyz; samesite=strict"
    // strict — только запросы с того же сайта
    // lax — разрешает GET при переходе по ссылке (по умолчанию)
    // none — всегда (требует secure)

    HttpOnly — недоступны из JavaScript

    Куки с флагом HttpOnly нельзя прочитать через document.cookie. Они устанавливаются сервером и существуют только в HTTP-запросах:

    Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Strict

    Это защищает от XSS: даже если злоумышленник выполнит JS, он не сможет украсть такую куку.

    Вспомогательные функции

    function setCookie(name, value, options = {}) {
      let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`
    
      if (options.maxAge) cookie += `; max-age=${options.maxAge}`
      if (options.expires) cookie += `; expires=${options.expires.toUTCString()}`
      if (options.path) cookie += `; path=${options.path}`
      if (options.domain) cookie += `; domain=${options.domain}`
      if (options.secure) cookie += '; secure'
      if (options.sameSite) cookie += `; samesite=${options.sameSite}`
      if (options.httpOnly) cookie += '; httponly'
    
      document.cookie = cookie
    }
    
    function getCookie(name) {
      const cookies = parseCookies(document.cookie)
      return cookies[name] ?? null
    }
    
    function deleteCookie(name, path = '/') {
      // Устанавливаем прошедшую дату — браузер удаляет куку
      document.cookie = `${encodeURIComponent(name)}=; max-age=0; path=${path}`
    }
    
    function parseCookies(cookieString) {
      if (!cookieString) return {}
      return cookieString
        .split('; ')
        .reduce((acc, pair) => {
          const idx = pair.indexOf('=')
          const key = decodeURIComponent(pair.slice(0, idx).trim())
          const val = decodeURIComponent(pair.slice(idx + 1))
          acc[key] = val
          return acc
        }, {})
    }

    Реальный пример: настройки пользователя

    // Сохранить язык интерфейса на 1 год
    setCookie('lang', 'ru', {
      maxAge: 365 * 24 * 60 * 60,
      path: '/',
      sameSite: 'lax',
    })
    
    // Прочитать при загрузке страницы
    const lang = getCookie('lang') || 'ru'
    applyLanguage(lang)
    
    // Удалить при выходе из аккаунта
    function logout() {
      deleteCookie('session_id')
      deleteCookie('csrf_token')
      window.location.href = '/login'
    }

    Типичные ошибки

    Ошибка 1: хранят чувствительные данные в куке без HttpOnly

    // Сломано: доступно через XSS-атаку
    document.cookie = 'session_token=abc123'
    
    // Исправлено: сессию устанавливает сервер с HttpOnly
    // Set-Cookie: session_token=abc123; HttpOnly; Secure; SameSite=Strict
    // HttpOnly — JS вообще не может прочитать эту куку

    Ошибка 2: не указывают path — кука видна не на всех страницах

    // Сломано: кука доступна только на /profile и ниже
    document.cookie = 'lang=ru'  // path по умолчанию — текущий путь
    
    // Исправлено: path=/ — кука видна везде
    document.cookie = 'lang=ru; path=/'

    Ошибка 3: парсят куки через split('=') — ломается при значениях с '='

    // Сломано: JWT-токен содержит '=' в base64
    // 'token=eyJ...abc=' → split('=') даст ['token', 'eyJ...abc', '']
    
    // Исправлено: используем indexOf('=') для нахождения первого '='
    const idx = pair.indexOf('=')
    const key = pair.slice(0, idx).trim()
    const val = decodeURIComponent(pair.slice(idx + 1))  // всё после первого '='

    Куки vs localStorage

    | | Куки | localStorage |

    |---|---|---|

    | Отправляются на сервер | Да, автоматически | Нет |

    | Размер | ~4 KB | 5-10 MB |

    | Срок жизни | Настраивается | До явного удаления |

    | HttpOnly | Да (недоступны из JS) | Нет |

    | Доступность | Браузер + сервер | Только браузер |

    В реальных проектах

  • Сессии: cookie с HttpOnly от сервера — стандарт аутентификации (Next-Auth, Passport.js)
  • Настройки: язык, тема, регион — хранятся в куках чтобы сервер мог рендерить правильную версию
  • A/B тесты: id варианта хранится в куке — сервер отдаёт нужную версию
  • GDPR: баннер с куками — обязательно для EU пользователей; consent-куки хранят согласие
  • Примеры

    Реализация вспомогательных функций getCookie, setCookie, deleteCookie

    // Вспомогательные функции для работы с куками
    
    function parseCookies(cookieString) {
      if (!cookieString.trim()) return {}
      return cookieString
        .split('; ')
        .reduce((acc, pair) => {
          const idx = pair.indexOf('=')
          if (idx === -1) return acc
          const key = decodeURIComponent(pair.slice(0, idx).trim())
          const val = decodeURIComponent(pair.slice(idx + 1))
          acc[key] = val
          return acc
        }, {})
    }
    
    function setCookie(name, value, options = {}) {
      let cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value)
    
      if (options.maxAge !== undefined) cookie += '; max-age=' + options.maxAge
      if (options.path) cookie += '; path=' + options.path
      if (options.domain) cookie += '; domain=' + options.domain
      if (options.secure) cookie += '; secure'
      if (options.sameSite) cookie += '; samesite=' + options.sameSite
    
      // В браузере: document.cookie = cookie
      return cookie  // возвращаем для демонстрации
    }
    
    function getCookie(cookieString, name) {
      const cookies = parseCookies(cookieString)
      return cookies[name] ?? null
    }
    
    function deleteCookie(name, path = '/') {
      return encodeURIComponent(name) + '=; max-age=0; path=' + path
    }
    
    // Демонстрация
    const cookieStr = 'session_id=abc123XYZ; lang=ru; theme=dark; user_id=42'
    
    const cookies = parseCookies(cookieStr)
    console.log('Все куки:', cookies)
    // { session_id: 'abc123XYZ', lang: 'ru', theme: 'dark', user_id: '42' }
    
    console.log('lang:', getCookie(cookieStr, 'lang'))    // 'ru'
    console.log('theme:', getCookie(cookieStr, 'theme'))  // 'dark'
    console.log('missing:', getCookie(cookieStr, 'role')) // null
    
    // setCookie — возвращает строку для document.cookie
    const newCookie = setCookie('lang', 'en', {
      maxAge: 365 * 24 * 60 * 60,
      path: '/',
      sameSite: 'lax',
    })
    console.log('\nСтрока для установки куки:')
    console.log(newCookie)
    // 'lang=en; max-age=31536000; path=/; samesite=lax'
    
    const deletionStr = deleteCookie('session_id')
    console.log('\nСтрока для удаления куки:')
    console.log(deletionStr)
    // 'session_id=; max-age=0; path=/'
    
    // Пример реального использования: язык интерфейса
    const userLang = getCookie(cookieStr, 'lang') || 'ru'
    console.log('\nЯзык интерфейса:', userLang)  // 'ru'

    Задание

    Напиши функцию parseCookies(cookieString), которая принимает строку в формате document.cookie ("key1=val1; key2=val2") и возвращает объект с парами ключ-значение. Значения должны декодироваться через decodeURIComponent.

    Подсказка

    cookieString.split('; ').reduce((acc, pair) => { const [k, v] = pair.split('='); acc[k.trim()] = decodeURIComponent(v); return acc }, {})

    Загружаем среду выполнения...
    Загружаем AI-помощника...