Una sesión que desaparece parece un problema de acceso y casi nunca lo es. El acceso funcionó; lo que se perdió fue la prueba de ese acceso, entre una petición y la siguiente.

1. Algo está limpiando los archivos de sesión

En Debian y Ubuntu, un temporizador barre los archivos de sesión de PHP según su propio calendario, usando la idea que tiene la DISTRIBUCIÓN de la duración, no la que su página fija con ini_set. Una página que sube gc_maxlifetime en tiempo de ejecución no cambia nada de ese barrido.

systemctl list-timers | grep phpsessionclean
php -i | grep -E "session.save_path|session.gc_maxlifetime"

El remedio es un directorio de sesiones propio que el barredor no toca, con su duración fijada en la configuración del pool y no en tiempo de ejecución.

php_admin_value[session.save_path] = /var/lib/php/sessions-mysite
php_admin_value[session.gc_maxlifetime] = 86400

2. La cookie no vuelve

session.cookie_secure = 1
session.cookie_httponly = 1
session.cookie_samesite = Lax
cookie_secure en un sitio alcanzable por http pelado significa que la cookie no se devuelve nunca, y el usuario queda desconectado al instante sin error en ninguna parte. Es correcto en un sitio solo https y fatal en uno mixto.

3. Dos servidores, una sesión

Detrás de un balanceador, la primera petición cae en el servidor A y escribe allí un archivo de sesión; la segunda cae en B, que no tiene ese archivo. El usuario se desconecta al azar, que es la versión más difícil de reproducir.

  • Sesiones en Redis o en la base de datos, compartidas por los dos.
  • O sesiones pegajosas en el balanceador: funciona, y no sobrevive a que se reemplace un servidor.

Véalo ocurrir

ls -la /var/lib/php/sessions/ | head
# log in, then look again: your file should be there and should stay
Si el archivo existe y el usuario sigue desconectado, es la cookie. Si el archivo desaparece, es el barredor. Esa sola observación parte el problema por la mitad.