«Invalid bitrix sessid» в корзине — но только у самых медленных покупателей
Часть анонимных покупателей интернет-магазина не могла добавить товар в корзину: «Ошибка: Invalid bitrix sessid». У сотрудников и в быстром тесте всё работало. Разбираем, как срок жизни сессии в 24 минуты незаметно ломал CSRF-токен — и почему воспроизвести баг было так сложно.
Симптом
Интернет-магазин на 1С-Битрикс, малый VPS. Жалоба: часть покупателей не может добавить товар в корзину — выскакивает Ошибка: Invalid bitrix sessid. Коварство в том, что у сотрудников и в быстром тесте «зашёл — положил в корзину» ошибка не воспроизводилась.
Разведка
Сначала отмели очевидное: композит, рестарт MySQL, OOM, кэш страниц — ни при чём. Зацепка нашлась в том, кто ловит ошибку: покупатели, которые долго изучают карточку перед добавлением.
Дальше по цепочке: примерно с середины месяца сессии на проекте стали храниться в БД (b_user_session / b_sec_session). CSRF-токен bitrix_sessid() жёстко привязан к session_id. А время жизни сессии — дефолтные PHP session.gc_maxlifetime = 1440, то есть 24 минуты.
Причина
Складывалось идеально «незаметно»: покупатель открывает карточку товара, отвлекается дольше 24 минут, garbage collector вычищает его сессию, сервер выдаёт новый session_id — и токен на странице перестаёт совпадать с сессией. При добавлении в корзину — Invalid bitrix sessid. Быстрый тестировщик в это окно никогда не попадал, поэтому баг и казался «плавающим».
Что сделали
Подняли время жизни сессии session.gc_maxlifetime с 1440 до 14400 (24 минуты → 4 часа). Правку внесли через .htaccess (php_value session.gc_maxlifetime 14400), потому что vhost на этом сервере под управлением Ansible и правится не руками. Проверили веб-пробой, что ini_get('session.gc_maxlifetime') вернул 14400 — без перезагрузки и без разлогина активных пользователей.
Результат
Окно жизни сессии выросло с 24 минут до 4 часов, и сбой у «медленных» покупателей исчез. Это классический пример, где симптом на витрине («не кладётся в корзину») и причина (дефолт PHP + переезд сессий в БД) лежат в совершенно разных слоях — и разница между «перезагрузили, вроде прошло» и реальным диагнозом стоит клиенту потерянных заказов.
Имя клиента не раскрываем по NDA. Цифры и подход — реальные.