Исправляем постоянное обновление PHPSESSID в битриксе на поддомене “www”
- Предыстория
- Причины
- 1 вариант решения проблемы (не рабочий!)
- 2 вариант решения проблемы (как показал опыт, работает не на 100% у всех)
- 3 вариант решения (на тестировании)
- Итоги
- Скачать модуль для исправления PHPSESSID
- Источники
Предыстория
В какой-то момент времени появилась на сайте странная проблема, из-за которой невозможно было авторизоваться на сайте ни в публичной части, ни в админке. Помогало только установить флажок “Запомнить меня” и тогда авторизация проходила успешно. Но, как оакзаалась на авторизации проблема не закончилась, а стали проявляться странные странности.
- При попытке что-либо сохранить, изменить или удалить в админке сайта ничего не получалось, страница в лучшем случае просто перезагружалась, в худшем случае вылезали разного рода ошибки о том, что невозможно сохранить изменения.
- При просмотре заказа в админке битрикса выводилось сообщение об ошибке “Ошибка обработки запроса. Наиболее вероятные причины: у пользователя недостаточно прав; проблемы с сохранением сессий PHP; часть данных POST-запроса обрезается PHP либо веб-сервером.”
- Стали вылезать проблемы в публичной части сайта на AJAX элементах. Например, перестала обновляться сумма корзины при изменении количества по любой из позиций. И иногда не подгружались блоки на страницах.
Причем было так же замечено, что в режиме приватного просмотра (он же режим инкогнито) все работало нормально и никаких проблем не возникало. Позже опытным путем было выявлено, что если почитстить кэш и куки тоже помогает, но не на долго, через какое-то время причина повторяется. Чуть позже выявли, что достаточно удалить тольок куки.
Пока дело касалось только админки все было вполне терпимо, но как только проблемы коснулись публичной части – стал докапываться до корня ))
Причины возникновения проблемы
Путем перечитывания десятка страниц в интернете со скудным упоминанием подобной проблемы удалось найти ниточку к разгадке данной задачи.
Оказалось что все дело в том, что в куках браузера сохраняется индетификатор PHP сессии и передается он в заголовках браузера каждый раз при обновлении страницы, или AJAX запросе. Однако дальше обнаружил, что в данных PHPSESSID в куках для домена оказлось несколько, т.к. сайт работает на домене www.site.ru. Вот эта вот приписка поддомена WWW в домене сайта и создает весь этот бардак. О том почему именно WWW дает такой эффект я отвечу чуть ниже в посте, а сейча расскажу что вообще происходит с ID сессии. Так как при первом визите на сайт в стартует создание PHP сессии session_start() и сохраняает в $_COOKIE[“PHPSESSID”]. Дальше все время пока активна сессия в $_COOKIE[“PHPSESSID”] и session_id() данные должны совпадать. В этом случае все работает в штатном режиме. Однако в какой-то момент при выходе из админки сайта в переменной $_COOKIE[“PHPSESSID”] вдруг появляется другой идентификатор, который не соответсвует требованиям битрикса и поэтому каждый раз при загрузке страницы обновляется идентификатор сессии, но по каким-то причинам он не сохраняется в куках. В $_COOKIE[“PHPSESSID”] зависает “битый” ID.
При детальном изучении стало понятно почему это происходит. В консоле браузера видно, что для домена www.site.ru есть две куки PHPSESSID: одна для хоста .www.site.ru, а вторая для хоста .site.ru. Так вот эта вторая кука и содержит битый ID и появляется она в какой-то момент при выходе из админки по кнопке “Выйти”.
1 вариант решения проблемы (не рабочий!)
Для решения данной проблемы нужно в файле dbconn.php добавить строку заменив в ней site.ru на ваш домен.
setcookie('PHPSESSID', '', time() - 100, '/', .site.ru);
Руководствуясь этой логикой попытался добавить проверку на расхождение PHPSESSID в куках и session_id().
if($_COOKIE["PHPSESSID"] != session_id())
setcookie('PHPSESSID', '', time() - 100, '/', .site.ru);
Казалось бы вот оно счастье, но не тут-то было! Данный способ не сможет работать, т.к. во время обработки файла dbconn.php еще не инициирована PHP сессия, поэтому session_id() будет всегда пустым. А просто безжалостно убивать PHPSESSID нет смысла.
Исходя из всего этого переходим к второму варианту решения проблемы.
2 вариант решения проблемы (как показал опыт, работает не на 100% у всех)
В битриксе PHP сессия стартует в файле /bitrx/modules/main/include.php.

На снимке экрана выше видно, что следом за инициацией PHP сессии отрабатывает вызов функций подписки на событие OnPageStart.
Помня о том, что желательно код ядра не модифицировать, добавляем в файле init.php свою функцию обработчик событие OnPageStart.
RegisterModuleDependences("main", "OnPageStart", "main", "MyCookieSession", "FixDoubleSessionId", "100");
class MyCookieSession
{
public static function FixDoubleSessionId()
{
if(
session_id()
&& isset($_COOKIE["PHPSESSID"])
&& $_COOKIE["PHPSESSID"] != session_id()
)
{
setcookie('PHPSESSID', '', time() - 100, '/');
header('Location: '.$_SERVER['REQUEST_URI]']);
}
}
}
Важный момент! После того, как мы понимаем, что ID сессии в куках и session_id() различны, нам нужно отправить браузеру заголовки о сбросе PHPSESSID в куках и второй заголовок, который будет перезагружать страницу. Если не перезагрузить страницу, то в браузер запишется устаревшее значение PHPSESSID, и тогда при следующей загрузке старницы опять будут расхождения.
Есть подводный камень, который нужно учесть и отладить на Вашем прокте, может возникать циклический редирект, что совсем не хорошо, поэтому важно убедиться в правильной работе данного кода на конкретном проекте.
3 вариант решения (на тестировании)
С течением времени выяснилось, что предыдущий способ работает, но не у всех, т.к. продолжали приходить жалобы, что у кого-то отображается “белый экран” вместо содержимого, которое должно было подгружаться через AJAX запрос. Конечно локальная очистка куков браузера помогала на 100% и больше проблем не возникало, но хочется же “сделать чудо”, чтобы никто из пользователей не обременялся чисткой куков, а все само волшебным образом заработало.
Шаг 1. Помощь техподдержки Битрикса
От того, что мысли на тему решения данной головоломки в голове закончились, решил привлечь техподдержку Битрикса на помощь. В итоге получил два совета: 1 – переместить хранение ID сессий в БД, и 2 – сделать настройку сайта по инструкции https://dev.1c-bitrix.ru/learning/course/?COURSE_ID=103&LESSON_ID=20670&LESSON_PATH=8799.3987.20670.
Для смены хранилища сессий в файле /bitrix/.settings.php добавляем следующий фрагмент кода:
'session' =>
array (
'value' =>
array (
'mode' => 'default',
'handlers' =>
array (
'general' =>
array (
'_fromSecurity' => true,
'type' => 'database',
),
),
),
'readonly' => true,
),
После тестов подолжительностью около 1,5 недели выявилось, что проделанной работы все еще не достаточно, и нужно придумать что-то еще.
Шаг 2. Переделка функции очистки куков с редиректом в init.php
Отрефликсировав предыдущие опыты и эксперименты я решил пойти по пути жесткого перебора значений хостов для хранений куки PHPSESSID. Для этго сделал вместо циклического пошаговый сброс куки.
RegisterModuleDependences("main", "OnPageStart", "main", "MyCookieSession", "FixDoubleSessionId", "100");
class MyCookieSession
{
public static function FixDoubleSessionId()
{
if(
env('CLEAR_COOCKIE_USE')
&& session_id()
&& isset($_COOKIE["PHPSESSID"])
&& $_COOKIE["PHPSESSID"] != session_id()
)
{
$needReload = false;
$curStep = 0;
$domain = "";
if(!isset($_COOKIE["SESS_BUG_STEP"]) || $_COOKIE["SESS_BUG_STEP"] == ""){
$domain = '.youdomain.ru';
$needReload = true;
$curStep = 1;
}
elseif($_COOKIE["SESS_BUG_STEP"] == "1"){
$domain = 'youdomain.ru';
$needReload = true;
$curStep = 2;
}
elseif($_COOKIE["SESS_BUG_STEP"] == "2"){
$domain = '.www.youdomain.ru';
$needReload = true;
$curStep = 3;
}
elseif($_COOKIE["SESS_BUG_STEP"] == "3"){
$domain = 'www.youdomain.ru';
$needReload = true;
$curStep = 4;
}
elseif($_COOKIE["SESS_BUG_STEP"] >= "4"){
$domain = '';
$needReload = true;
$curStep++;
}
if($needReload){
setcookie('SESS_BUG_STEP', $curStep, time() + 100, '/');
setcookie('PHPSESSID', '', time() - 100, '/', $domain);
header('Location: '.$_SERVER['REQUEST_URI]']);
}
}
}
}
Этот вариант является пересборкой варианта 2, в нем я добавил перебор разных вариантов доменов для хранения куки в браузере, чтобы последовательно очистить их всех в порядке приоритета срабатывания. Все это дело конечно же начал логировать и смотреть на каком шаге прекращается перебор домена и начинается стабильная работа сайта.
Таким образом опытным путем пришел к следующему приоритету:
- “.youdomain.ru“,
- “youdomain.ru“,
- “.www.youdomain.ru“,
- “www.youdomain.ru“.
Итоги
Данный вариант помог мне решить проблему слетающих PHPSESSID.
Повторюсь, этот способ актуален для сайтов, которые работают на домене с WWW в качестве основного домена.
Модуль исправления PHPSESSID для Битрикса
В виду популярности вопроса я решил создать модуль для Битрикса, который помогает исправить дубли PHPSESSID. Пользуйтесь на здоровье! Буду рад видеть ваши отзывы и комментарии ))

Бесплатно скачать и установить модуль FIX PHPSESSID из маркетплейса Битрикса
Источники
- https://dev.1c-bitrix.ru/community/blogs/howto/955.php
- https://dev.1c-bitrix.ru/community/webdev/user/1064429/blog/40425/
- Техподдержка Битрикса
- https://dev.1c-bitrix.ru/learning/course/?COURSE_ID=103&LESSON_ID=20670&LESSON_PATH=8799.3987.20670
Если статья оказалась полезной для Вас, поставьте пожлауйтса лайк, это будет сигналом для меня, что я двигаюсь в правильном направлении и приношу пользу людям.