Случайные разлогинивания в продакшене: исправления cookie и сессий
Случайные разлогинивания в продакшене часто вызваны настройками cookie и прокси. Узнайте, как SameSite, Secure, domain и заголовки могут тихо обрывать сессии.

Почему после релиза появляются случайные разлогинивания
Сессия — это то, как ваше приложение запоминает пользователя между загрузками страниц. Чаще всего это запоминается в cookie. Cookie либо содержит подписанный токен (часто при использовании JWT), либо содержит идентификатор сессии, который указывает на данные пользователя на сервере.
«Случайные» разлогинивания обычно означают, что браузер перестал отправлять cookie сессии обратно в приложение. Ничего не падает. Сервер просто перестаёт видеть действительную сессию, считает пользователя посторонним и отправляет на страницу входа.
Именно поэтому на localhost всё может выглядеть нормально, а в продакшене — разваливаться. Локальное тестирование часто проходит по обычному HTTP, на одном хосте и без прокси. В продакшене добавляются реальные домены, HTTPS, CDN и обратные прокси. Эти изменения влияют на то, как браузеры хранят cookie и когда прикрепляют их к запросам.
Бесшумная потеря сессии часто выглядит так:
- Вы входите в систему, а затем вас выкидывает после обновления или нескольких кликов.
- В одной вкладке вы оставались залогинены, в другой — перебрасывает на страницу входа.
- Это происходит только в продакшене или только на мобильных/в Safari.
- Нет полезных ошибок в UI, только «пожалуйста, войдите снова».
AI‑созданные прототипы особенно подвержены этому, потому что часто идут с дефолтными настройками cookie и копируемыми сниппетами, которые работали в превью‑окружении. Самые распространённые триггеры — несовпадающий Domain/Path, дефолты SameSite, неверная установка Secure и проблемы с заголовками прокси, когда приложение не понимает, что пользователь на самом деле использует HTTPS.
Если вы унаследовали кодовую базу, сгенерированную ИИ, которая «в основном работает», но постоянно выкидывает пользователей — это обычно одна из первых областей для аудита перед переписыванием аутентификации.
Основы cookie, от которых зависит выживание сессии
Большинство проблем с сессиями сводятся к нескольким атрибутам cookie. Если хотя бы один из них неверен, браузер может тихо перестать отправлять cookie, идентифицирующее пользователя.
Cookie имеет область действия. Самые важные части:
- Domain: какие хосты могут получать cookie. Если вы выставили неправильный домен или забыли, что
wwwи корневой домен — разные хосты, cookie не будет отправлено на некоторых запросах. - Path: какие пути URL получают cookie. Если он слишком узкий, страницы вне этого пути будут вести себя как разлогиненные.
- Expiration: как долго оно действует. Отсутствие или короткий срок действия может сделать вход нестабильным, особенно после обновления или перезапуска браузера.
Есть два распространённых срока жизни. Session cookies обычно исчезают при закрытии браузера. Persistent cookies имеют явную дату истечения и используются для «запомнить меня». Если приложение ожидает постоянный логин, но по ошибке выдаёт session cookie, пользователи будут говорить, что их «выкинуло без причины».
Флаг Secure прост, но нетерпим: когда он включён, браузеры отправляют cookie только по HTTPS. Это правильно для продакшена, но ломается, если приложение думает, что работает по HTTP (часто из‑за настроек прокси).
SameSite контролирует, когда cookie отправляется через «границы сайтов»:
- Lax: отправляется при большинстве обычных навигаций, но не в ряде встроенных или кросс‑сайтовых потоков.
- Strict: отправляется только на том же сайте. Более безопасно, но может ломать редиректы при входе.
- None: отправляется кросс‑сайтово, но при этом обязательно должно быть
Secure.
Один неверный атрибут может заставить cookie «исчезнуть». Cookie может всё ещё храниться в локальном хранилище, но не прикрепляться к важным запросам. Сервер создаёт новую сессию — и пользователь выглядит разлогиненным.
Настройки SameSite, которые молча разлогинивают
SameSite — это правило cookie, которое говорит браузеру, когда можно отправлять вашу сессионную cookie. Если оно слишком строгое, cookie пропускается на некоторых запросах, и приложение выглядит так, будто «случайно» выкидывает пользователей.
Типичный пример: фронтенд на app.example.com, API на api.example.com. Если при логине сессионная cookie установлена так, что браузер считает её кросс‑сайтовой, следующий запрос может не включать эту cookie. API тогда думает, что пользователь новый, и создаёт новую сессию.
Браузерные по умолчанию ужесточались за последние годы. Множество старых руководств предполагало, что cookie отправляются более вольно, поэтому конфигурация «работает на localhost» превращается в разлогинивания в продакшене, как только появляются реальные домены, HTTPS и сложные потоки с редиректами.
Что чаще всего ломается
Проблемы проявляются в потоках, которые не являются простым переходом по ссылке на одном сайте: OAuth‑логины (Google, GitHub), магические ссылки в письмах, встроенные виджеты на другом сайте и некоторые сценарии с субдоменами, где редиректы прыгают между хостами.
В большинстве обычных приложений SameSite=Lax хватает. Lax обычно позволяет cookie при верхнеуровневой навигации (например, переход по ссылке), но блокирует их во многих фоновых или встроенных запросах. Если ваш поток аутентификации зависит от cookie во время callback‑редиректа или внутри iframe, Lax может не сработать.
Обычно нужен SameSite=None, когда cookie должен отправляться в явно кросс‑сайтовом контексте (встраиваемые виджеты, некоторые OAuth‑подстановки и определённые кросс‑доменные паттерны API). Но SameSite=None имеет жёсткое требование: он должен идти вместе с Secure, то есть cookie будет отправляться только по HTTPS.
Практическое правило:
- По умолчанию используйте
SameSite=Laxдля стандартных одно‑доменных приложений. - Для встраиваний, кросс‑сайтовых запросов или сложных OAuth‑callback используйте
SameSite=None; Secure. - Избегайте
SameSite=Strictдля сессионных cookie входа, если вы не полностью понимаете побочные эффекты. - После изменения SameSite протестируйте полный поток входа на реальном продакшн‑домене.
Флаги Secure и подводные камни HTTPS
Флаг Secure говорит браузеру отправлять cookie только по HTTPS. Это хорошо для безопасности, но создаёт путаницу, когда к приложению всё ещё может попасть HTTP‑трафик.
Классическая ошибка: пользователь попал на HTTP‑версию домена, сервер пытается установить Secure cookie и затем делает редирект на HTTPS. Браузеры игнорируют Secure cookie, установленные по HTTP. Редирект проходит, но сессия не сохраняется — пользователь выглядит разлогиненым сразу же.
Локальное тестирование это скрывает, так как всё может работать по HTTP на localhost. Вы деплоитесь за HTTPS, включаете Secure, и часть пользователей начинает терять сессии, потому что на первом запросе срабатывает редирект или правило на краю, которое ещё допускает HTTP.
Различия между staging и production
Staging‑окружение часто имеет другие URL, TLS‑настройки или правила прокси. Если staging принудительно использует HTTPS, а production разрешает и HTTP, поведение cookie будет разным.
Также следите за случаями, когда приложение думает, что работает по HTTP, потому что TLS терминируется на прокси. Если приложение не доверяет переадресованным заголовкам, оно может выставлять cookie и редиректы, основываясь на неверной схеме.
Как понять, что браузер отвергает cookie
Типичные симптомы:
- В ответе вы видите
Set-Cookie, но cookie не появляется в хранилище браузера. - Вход «работает» для одного запроса, а затем следующая страница загружается как разлогиненная.
- Затронуты лишь некоторые пользователи (часто те, кто использует старую закладку или вводит домен без HTTPS).
- DevTools показывает предупреждения вроде «This Set-Cookie was blocked» или «Secure cookie over insecure connection.»
Если кодовая база сгенерирована ИИ и cookie устанавливаются в нескольких местах, часто встречаются дубликаты или конфликтующие настройки, из‑за чего поведение кажется случайным, пока вы не проследите весь первый запрос.
Ошибки в Domain и Path, приводящие к потере сессии
Иногда браузер делает ровно то, что вы ему сказали — просто это не соответствует вашему намерению.
По умолчанию cookie привязаны к хосту: cookie создаётся только для того хоста, который её установил (например, app.example.com). Domain‑cookie шире: если вы выставите Domain=example.com, cookie будет отправляться и на app.example.com, и на api.example.com, и на другие поддомены. Ни один из вариантов не «лучше» по умолчанию, но смешивание их между сервисами может приводить к разрывам сессии.
Субдомены: классический разрыв, ломающий сессии
Если фронтенд на app.example.com, а API на api.example.com, host‑only cookie, установленное API, не будет отправляться на фронтенд и наоборот. Пользователь выглядит залогиненным на одной стороне и разлогиненным на другой.
Также следите за неправильно выставленными значениями Domain. Domain=localhost — не рабочая настройка для продакшена, а Domain=www.example.com не покроет app.example.com. Если вы используете кастомный домен или CDN, «реальный» хост, который видит браузер, может отличаться от того, что думает ваше приложение.
Path: мелочь с большим эффектом
Cookie отправляется только на URL, совпадающие с его Path. Если вы задали Path=/api, а приложение затем переходит на /dashboard, это cookie туда не пойдёт. Такое случается, когда cookie ставят в одном маршруте, а читают в другом.
Несколько простых проверок:
- Убедитесь, что Domain cookie совпадает с теми хостами, куда вы хотите её отправлять.
- Ставьте
Domain=example.comтолько если действительно нужна общая сессия между поддоменами. - Обычно держите
Path=/, если нет веской причины иначе. - Тестируйте логин на всех поддоменах и на «голом» и на
www‑доменах.
Заголовки обратного прокси, которые ломают cookie в продакшене
Многие приложения в продакшене сидят за обратным прокси (Cloudflare, Nginx, Vercel, Render, Fly.io, Railway). Прокси терминирует HTTPS и пересылает запрос в приложение. Если приложение не знает, что оно за прокси, оно может думать, что все запросы — plain HTTP, или что хост — внутреннее имя. Это проявляется как «случайные» разлогинивания.
Обычно причина проста: приложение формирует настройки cookie из увиденного запроса. Если оно думает, что запроса пришёл по HTTP, оно может не ставить Secure или сгенерировать неверный URL для редиректа. Если оно думает, что хост другой — поставит cookie для неправильного домена. Браузер тогда игнорирует cookie или хранит её в области, не подходящей для реального сайта.
Эти переадресованные заголовки говорят приложению правду о внешнем запросе:
X-Forwarded-Proto: исходная схема (HTTP vs HTTPS)X-Forwarded-Host: исходный хост (ваш реальный домен)X-Forwarded-For: исходный IP клиента (часто используется для лимитов и правил безопасности)
Конкретный пример: прокси присылает X-Forwarded-Proto: https, но приложение игнорирует это. Мидлвар сессий думает, что схема — HTTP, и ставит нес Secure cookie. На HTTPS‑сайте браузеры могут отклонить или некорректно обработать такое cookie, и пользователи теряют сессию после обновления или редиректа.
Часто исправление — одна настройка фреймворка (часто называется «trust proxy»). Важно: доверять заголовкам прокси от открытого интернета небезопасно. Доверяйте им только если перед вашим приложением реально стоит единственный прокси, до которого нельзя добраться напрямую.
Пошагово: правильно выставить cookie и настройки сессии
Если вы видите случайные разлогинивания в продакшене, не угадывайте. Меняйте одну переменную за раз, чтобы понять, что действительно помогло.
Начните с фиксации реального публичного адреса. Запишите точный URL, который вводят пользователи (включая, используете ли вы www). Подтвердите, всегда ли сайт доступен по HTTPS с первого запроса и может ли кто‑то попасть на HTTP‑эндпойнт до редиректа.
Далее решите, действительно ли вам нужны кросс‑сайтовые cookie. Они обычно нужны, когда аутентификация проходит на другом домене (часто в OAuth), когда API на отдельном субдомене и вы делите сессии, или когда приложение встраивается в другой сайт.
Затем приведите настройки cookie в соответствие с реальностью:
- Для одно‑сайтовых приложений
SameSite=Laxчасто достаточно. - Для кросс‑сайтовых потоков может потребоваться
SameSite=None, но только в паре сSecure. - Устанавливайте
Secure, опираясь на то, что видят пользователи (HTTPS), а не на то, что думает сервер. - Будьте осознанны с
DomainиPath. Если нет необходимости делиться сессией между поддоменами, не ставьтеDomain. Обычно держитеPath=/.
Наконец, решите самую частую причину «работает локально»: прокси. Проверьте, что прокси пересылает правильные заголовки, и что приложение настроено их читать. Тестируйте как новый пользователь: окно инкогнито, полная перезагрузка браузера и весь поток входа вместе с редиректами.
Частые ловушки (особенно в AI‑сгенерированных кодовых базах)
AI‑сгенерированные приложения часто работали на localhost, а в продакшене люди начинают терять сессии. Разрыв почти всегда связан с изменением поведения cookie при появлении реальных HTTPS, доменов и обратного прокси.
Тихая проблема, которая часто повторяется: выставляют SameSite=None без Secure. Многие браузеры проигнорируют такое cookie полностью, поэтому сессия никогда не закрепится. Вы видите Set-Cookie в ответе, но браузер его не сохраняет — и следующий запрос приходит без сессии.
Ошибки в Domain — ещё одна большая причина. Генераторы иногда подставляют домен превью или localhost. Если cookie ограничено неправильным хостом или слишком широко сфокусировано, браузер не отправит её туда, где приложение этого ожидает.
Обработка HTTPS на краю тоже запутывает: если TLS терминируется на балансировщике нагрузки, приложение может думать, что запросы приходят по HTTP, если вы не учли пересылаемые заголовки. Это ведёт к cookie и редиректам, не соответствующим тому, что делает браузер.
Наконец, AI‑кодовые базы часто смешивают системы аутентификации: системная сессия фреймворка плюс пользовательский JWT‑cookie или несколько мидлварей, которые меняют cookie. Это даёт поведение «последний записавший выигрывает», что ощущается как случайность.
Быстрый способ найти причины — сравнить атрибуты cookie между локалом и продакшеном (SameSite, Secure, Domain, Path) и проследить, что происходит во время редиректов после входа.
Быстрые проверки перед изменениями
Прежде чем менять флаги cookie или переписывать аутентификацию, потратьте пару минут на подтверждение текущего поведения. Многие «сессия истекла» — на самом деле «браузер перестал отправлять cookie.»
Если воспроизвести проблему можно за пару минут, откройте DevTools (Application/Storage) и наблюдайте cookie сессии после логина. Базовый сценарий:
- Войдите и подтвердите, что cookie сессии появилась.
- Обновите страницу дважды и убедитесь, что cookie всё ещё есть.
- Вызовите API и проверьте, что cookie отправляется с запросом.
- Выйдите и войдите снова — убедитесь, что не получилось двух похожих cookie.
Затем проверьте, всегда ли первый запрос идёт по HTTPS. Если первый хит — HTTP и затем редирект, cookie может быть установлена так, что браузер её отклонит.
Если приложение и API живут на разных субдоменах или доменах — запишите это. Кросс‑сайтовые правила и область cookie часто объясняют, почему логины «иногда» сохраняются.
Также подтвердите, что сервер думает о внешнем URL правильно. За прокси неверные forwarded‑заголовки могут заставить приложение думать, что схема или хост другие.
И в конце сравните браузеры. Если проблема только на мобильных или только в Safari, подозревайте SameSite и правила кросс‑доменных cookie.
Пример: в превью всё работает, в продакшене — разлогинивания
Типичный сценарий: AI‑сгенерированное приложение на app.example.com обращается к API на api.example.com, и оба стоят за обратным прокси. Логин выглядит успешным, но сессия не приживается.
В превью всё было на одном временном хосте — cookie простые, редиректы оставались в пределах origin.
После запуска люди заходят, видят дэшборд, а затем после редиректа возвращаются на страницу входа (часто после OAuth, магической ссылки из письма или шага «завершить регистрацию»). Нет явной ошибки, просто тихая потеря сессии.
Обычно это сочетание нескольких проблем:
- API ставит cookie с
SameSite=None, но безSecure. - Прокси не пересылает
X-Forwarded-Proto, или приложение не доверяет ему. - Сервер думает, что запрос по HTTP, и потому не ставит Secure cookie или генерирует редиректы между HTTP и HTTPS.
Современные браузеры требуют Secure для SameSite=None. Если Secure отсутствует, cookie не сохранится, хотя ответ утверждает, что он поставлен.
Обычно простое и решительное исправление:
- Убедиться, что прокси пересылает правильные заголовки (особенно
X-Forwarded-Proto). - Использовать
SameSite=None; Secureтам, где нужен кросс‑сайтовый доступ. - Подтвердить область cookie: выставлять Domain только при реальной нужде, держать
Path=/.
Следующие шаги: стабилизировать ваш AI‑прототип в продакшене
Если после корректировки флагов cookie вы всё ещё видите разлогинивания, пора перестать гадать и провести целенаправленный аудит аутентификации/сессий. Одна неверная догадка (неверный прокси‑заголовок или cookie на неправильном хосте) может сорвать несколько других «правильных» исправлений, которые работали в превью.
Прежде чем менять код, соберите набор фактов:
- Точные атрибуты устанавливаемых cookie (имя, Domain, Path, SameSite, Secure, HttpOnly, Max‑Age)
- Переменные окружения, связанные с auth/sessions (секрет сессии, базовый URL, домен cookie, доверенные прокси)
- Настройка обратного прокси/CDN и какие заголовки они добавляют (особенно
X-Forwarded-ProtoиX-Forwarded-Host) - Чёткие шаги воспроизведения (устройство/браузер, поток логина, какая страница триггерит проблему)
- Пару примеров запрос/ответ из продакшена (ответ на логин и первый неудачный запрос)
Если это AI‑прототип из инструментов вроде Lovable, Bolt, v0, Cursor или Replit, проблемы с cookie и прокси часто идут вместе с более крупными проблемами: смешанные схемы auth и утекающие секреты.
Если хотите второе мнение — FixMyMess (fixmymess.ai) специализируется на том, чтобы брать сломанные AI‑прототипы и доводить их до продакшена. Короткий аудит обычно позволяет точно найти несоответствие cookie или прокси, после чего логины перестают «прыгать».
Часто задаваемые вопросы
Почему моё приложение разлогинивает пользователей «случайно» после запуска?
Большинство «случайных» разлогиниваний происходят потому, что браузер перестаёт отправлять cookie сессии. Сервер не падает — он получает запрос без действительной сессии и считает пользователя неаутентифицированным.
Почему на localhost всё работает, а в продакшене — нет?
На localhost обычно один хост, часто HTTP и без обратного прокси. В продакшене появляются реальные домены, HTTPS, редиректы, CDN и прокси — и всё это меняет поведение хранения и отправки cookie браузером.
Как быстро подтвердить, что разлогинивание — это проблема с cookie?
Откройте DevTools и проверьте cookie сессии после входа и после перезагрузки. Убедитесь, что он существует, имеет ожидаемые атрибуты (Domain, Path, SameSite, Secure, срок) и реально отправляется в запросах, где это нужно.
Какой SameSite стоит использовать, чтобы остановить разлогинивания?
Для обычного односайтового приложения чаще всего достаточно SameSite=Lax — он сохраняет доступность логинов и даёт базовую защиту. SameSite=None нужен только для явно кросс‑сайтовых сценариев и всегда должен идти с Secure.
Почему `SameSite=None` иногда делает сессии невидимыми?
Браузер проигнорирует cookie с SameSite=None, если у него не установлен флаг Secure. В результате ответ будет содержать Set-Cookie, но браузер его не сохранит — поэтому следующий запрос приходит без сессии.
Как флаг Secure может вызвать немедленное разлогинивание после входа?
Cookie с Secure хранится и отправляется только по HTTPS. Если первый запрос приходит по HTTP (даже если потом идёт редирект на HTTPS), браузер может отвергнуть cookie, и пользователь сразу же окажется разлогинен после редиректа.
При чём здесь настройка «trust proxy» и стабильность сессий?
Если приложение за прокси и не доверяет переадресованным заголовкам, оно может считать запросы HTTP или видеть неправильный хост. Тогда cookie и редиректы формируются с неверной схемой/доменом — браузер их не отправит стабильно.
Может ли неправильный Domain или Path вызывать разлогинивания на отдельных страницах?
Да. Если cookie создано только для одного хоста (например, www), другие страницы не будут его отправлять. Если Path слишком узкий (например, /api), страницы вне этого пути не получат cookie — и пользователи будут казаться разлогиненными на этих маршрутах.
Почему я остаюсь залогинен на одном субдомене, но не на другом?
Да. Host‑only cookie, установленное на api.example.com, не будет отправлено на app.example.com. Чтобы разделы приложения видели одну и ту же сессию, нужно сознательно настроить Domain и SameSite.
Чем отличаются AI‑сгенерированные приложения и как FixMyMess может помочь?
Проекты, сгенерированные ИИ, часто смешивают подходы к аутентификации (разные слои сессий, JWT плюс системные сессии) и используют флаги cookie, которые работали только в превью. Быстрая аудит‑проверка атрибутов cookie, заголовков прокси и редиректов обычно показывает точную причину. FixMyMess (fixmymess.ai) специализируется на том, чтобы брать такие AI‑прототипы и доводить их до продакшена.