Pourquoi votre session PHP dure-t-elle plus longtemps que prévu ?
\n\nVous êtes-vous déjà demandé pourquoi, après 30 minutes d'inactivité, votre site se souvient encore que vous êtes connecté ? Ou, à l'inverse, vos utilisateurs se plaignent d'être déconnectés trop tôt ? La gestion de la durée de vie des sessions en PHP est l'un des casse-têtes les plus fréquents pour les développeurs, débutants comme confirmés. La question "How do I expire a PHP session after 30 minutes?" cumule des milliers de vues sur Stack Overflow, et ce n'est pas un hasard.
\n\nVoyons ensemble comment forcer une session PHP à expirer précisément au bout de 30 minutes. Nous allons explorer trois approches : de la plus simple (mais trompeuse) à la plus professionnelle. Vous découvrirez pourquoi session.gc_maxlifetime ne fonctionne souvent pas comme prévu et comment écrire un code robuste qui ne vous lâchera pas.
Le problème : qu'est-ce que la "durée de vie d'une session" en réalité ?
\n\nAvant de plonger dans le code, clarifions les bases. Une session PHP n'est pas simplement un minuteur qui tourne dans le navigateur. C'est un fichier sur le serveur (généralement dans /tmp/) qui stocke les données de l'utilisateur. "Faire expirer" une session signifie que ce fichier est supprimé ou rendu invalide.
Trois éléments clés influencent la durée de vie :
\n\n- \n
- Le cookie de session : le navigateur stocke l'identifiant de session (souvent dans le cookie
PHPSESSID). Ce cookie a sa propre durée de vie. \n - Le fichier de session sur le serveur : il possède une date de dernière modification (mtime). \n
- Le ramasse-miettes (GC) : PHP nettoie périodiquement les anciens fichiers de session, mais pas selon un calendrier fixe : il le fait de manière aléatoire. \n
C'est cette architecture "à trois niveaux" qui crée la confusion. Modifier un seul paramètre ne suffit souvent pas à obtenir l'effet désiré.
\n\nMéthode 1 : L'approche naïve — uniquement session.gc_maxlifetime
\n\nLa méthode la plus évidente, suggérée par la documentation, est de définir session.gc_maxlifetime. On pourrait croire que c'est la réponse à la question "How do I expire a PHP session after 30 minutes?". Mais ce n'est pas si simple.
<?php\n// Démarrage de la session\nsession_start();\n\n// Définit la durée de vie de la session en secondes (30 minutes = 1800 secondes)\nini_set('session.gc_maxlifetime', 1800);\n\n// Définit la durée de vie du cookie de session également à 30 minutes\nsession_set_cookie_params(1800);\n\n// C'est tout ? Non.\n?>\n\nPourquoi cela peut ne pas fonctionner ?
\n- \n
- Le ramasse-miettes (GC) est paresseux. Il s'exécute avec une probabilité de
session.gc_probability/session.gc_divisor(par défaut 1/100). Même si un fichier de session a une heure, il peut ne pas être supprimé lors de la prochaine requête si le GC ne s'est pas déclenché. \n - D'autres applications. Si plusieurs sites tournent sur le même serveur, ils peuvent nettoyer les sessions des autres, ou au contraire, ne pas nettoyer les vôtres. \n
- La mise en cache. Sur certains hébergements (notamment les hébergements mutualisés), les paramètres
ini_setpeuvent être ignorés. \n
Verdict : Cette méthode est une base, mais pas une solution miracle. Elle ne fonctionne que dans des conditions idéales. Pour un environnement de production, il faut creuser plus loin.
\n\nMéthode 2 : Un minuteur manuel dans la session (recommandée)
\n\nCette approche est le juste milieu. Au lieu de compter sur le ramasse-miettes, nous contrôlons nous-mêmes le temps écoulé depuis la dernière activité de l'utilisateur. C'est la réponse la plus populaire sur Stack Overflow concernant le timeout de session PHP après 30 minutes.
\n\nL'idée est simple : à chaque requête, nous vérifions quand l'utilisateur a été actif pour la dernière fois. Si plus de 30 minutes se sont écoulées, nous détruisons la session.
\n\n<?php\nsession_start();\n\n// Durée de vie de la session en secondes\n$session_lifetime = 1800; // 30 minutes\n\n// Vérifie si l'horodatage de la dernière activité est défini\nif (isset($_SESSION['LAST_ACTIVITY'])) {\n // Calcule la différence entre l'instant présent et la dernière activité\n $inactive_time = time() - $_SESSION['LAST_ACTIVITY'];\n \n if ($inactive_time > $session_lifetime) {\n // Session expirée : on la détruit\n session_unset();\n session_destroy();\n \n // Redirige vers la page de connexion\n header('Location: login.php?expired=1');\n exit();\n }\n}\n\n// Met à jour l'horodatage de la dernière activité\n$_SESSION['LAST_ACTIVITY'] = time();\n?>\n\nPourquoi cette méthode est-elle fiable ?
\n- \n
- Contrôle total : vous décidez exactement quand la session expire, sans dépendre du ramasse-miettes. \n
- Simple et portable : fonctionne sur tous les serveurs et hébergements. \n
- Facile à personnaliser : vous pouvez adapter la durée ou ajouter des conditions spécifiques. \n
Inconvénient : si l'utilisateur ferme son navigateur et le rouvre, la session peut encore être active (car le fichier n'est pas supprimé). Pour une sécurité renforcée, combinez cette méthode avec une expiration du cookie.
\n\nMéthode 3 : La solution professionnelle — stockage en base de données
\n\nPour les applications à haute exigence de sécurité ou de scalabilité, la meilleure approche consiste à stocker les sessions dans une base de données. Cela vous donne un contrôle granulaire sur la durée de vie et permet de gérer les sessions à l'échelle.
\n\n<?php\n// Configuration de la session pour utiliser une base de données\n// (exemple avec PDO et MySQL)\n\nsession_set_save_handler(\n 'open',\n 'close',\n 'read',\n 'write',\n 'destroy',\n 'gc'\n);\n\nfunction open($savePath, $sessionName) {\n // Connexion à la base de données\n global $pdo;\n $pdo = new PDO('mysql:host=localhost;dbname=sessions', 'user', 'password');\n return true;\n}\n\nfunction read($sessionId) {\n global $pdo;\n $stmt = $pdo->prepare('SELECT data FROM sessions WHERE id = ? AND expiry > ?');\n $stmt->execute([$sessionId, time()]);\n $row = $stmt->fetch();\n return $row ? $row['data'] : '';\n}\n\nfunction write($sessionId, $data) {\n global $pdo;\n $expiry = time() + 1800; // 30 minutes\n $stmt = $pdo->prepare('REPLACE INTO sessions (id, data, expiry) VALUES (?, ?, ?)');\n return $stmt->execute([$sessionId, $data, $expiry]);\n}\n\nfunction destroy($sessionId) {\n global $pdo;\n $stmt = $pdo->prepare('DELETE FROM sessions WHERE id = ?');\n return $stmt->execute([$sessionId]);\n}\n\nfunction gc($maxlifetime) {\n global $pdo;\n $stmt = $pdo->prepare('DELETE FROM sessions WHERE expiry < ?');\n return $stmt->execute([time()]);\n}\n?>\n\nAvantages :
\n- \n
- Sécurité maximale : les sessions expirent exactement à l'heure. \n
- Scalabilité : parfait pour les applications avec plusieurs serveurs. \n
- Nettoyage automatique : le ramasse-miettes est efficace et prévisible. \n
Inconvénient : plus complexe à mettre en place et nécessite une base de données.
\n\nConclusion : quelle méthode choisir ?
\n\nLe choix dépend de vos besoins :
\n- \n
- Pour un petit site ou un projet personnel : la méthode 2 (minuteur manuel) est parfaite. Simple, fiable, et rapide à implémenter. \n
- Pour un site en production avec du trafic modéré : combinez la méthode 2 avec une configuration correcte de
session.gc_maxlifetimeet du cookie. \n - Pour une application critique ou à grande échelle : optez pour la méthode 3 (base de données). \n
N'oubliez pas : la gestion des sessions est un pilier de la sécurité de votre application. Prenez le temps de bien la configurer, et vos utilisateurs vous remercieront.