◆ fedora のデフォルト設定だと 0.1% の確率で GC される
◆ 24 分以上前のセッションファイルは消される

思ってたより遥かに短かった

Node.js だとクライアント側にセッションデータをもたせることが多いですが PHP は標準機能でサーバ側にセッションデータを保存します
シークレットモードなど Cookie を保存しない方法でアクセスすると毎回新規にセッションデータを作成するのでファイル数やセッションデータの容量がすごいことになりそうです
セッション数や保存期間などなんらかの削除ルールはあると思いますが 気にしたことがなかったので調べてみました
PHP の設定を探してみると session に gc という項目がありました

# php -i | grep session.gc
session.gc_divisor => 1000 => 1000
session.gc_maxlifetime => 1440 => 1440
session.gc_probability => 1 => 1

maxlifetime が 1440 になっていました
1440 日かー 4 年くらいだし まあそんなものなのかなと思っていると

https://www.php.net/manual/ja/session.configuration.php#ini.session.gc-maxlifetime
session.gc_maxlifetime は、データが 'ごみ' とみなされ、消去されるまでの秒数を指定します。
ガベージコレクション (ごみの収集) は、 セッションの開始時に行われます (session.gc_probability と session.gc_divisor に依存します)。

まさかの秒
たった 24 分で消えるの?

でも これまで使っていた限りで 30 分もせずに消えた覚えはないです
関連する gc 設定も見てみると 消去プロセスは毎回実行されるわけではなく 確率で実行されるようです
その確率は gc_probability/gc_divisor で決まるようです
デフォルト値だと 1/1000 なので 0.1% です
ドキュメントだと session.gc_divisor のデフォルト値は 100 らしいですが私が試した環境だと 1000 でした

fedora で標準リポジトリからインストールした PHP8.0 です

; Defines the probability that the 'garbage collection' process is started on every
; session initialization. The probability is calculated by using the following equation:
; gc_probability/gc_divisor. Where session.gc_probability is the numerator and
; session.gc_divisor is the denominator in the equation. Setting this value to 100
; when the session.gc_probability value is 1 will give you approximately a 1% chance
; the gc will run on any given request. Increasing this value to 1000 will give you
; a 0.1% chance the gc will run on any given request. For high volume production servers,
; this is a more efficient approach.
; Default Value: 100
; Development Value: 1000
; Production Value: 1000
; http://php.net/session.gc-divisor
session.gc_divisor = 1000

試してみる

0.1% で自分しかアクセスしないなら 実行されてないのも分からなくはないです
でも 1, 2 回くらいはあってもいいんじゃないかなとも思います
本当に消えるのか信じがたいところもあったので 試してみました

こんな PHP ファイルを配置します

<?php
session_start();

$count = $_SESSION['count'] ?? 0;
$_SESSION['count'] = $count + 1;
?><!doctype html>

<h1><?= date('H:i:s') ?><br/><?= $count ?></h1>

アクセスした回数をカウントします
回数はセッションに保存します

このページを開いて何度かリロードすると そのたびにカウントが増えていきます
サーバ側でセッションが保存されるフォルダを見ると

# ll /var/lib/php/session/
total 4
-rw------- 1 apache apache 11 Jan 29 14:36 sess_p0vcmu48g8tfsbm329p5hol5k5

1 件だけファイルがあります

30 分ほど待ってから別のブラウザで開きます

# ll /var/lib/php/session/
total 8
-rw------- 1 apache apache 10 Jan 29 15:06 sess_6jbr0nb9emapvlknsbpsv2ojgv
-rw------- 1 apache apache 11 Jan 29 14:36 sess_p0vcmu48g8tfsbm329p5hol5k5

別ブラウザからなので別セッションになって新しくファイルが増えました
新たに開いたほうのブラウザで 0.1% の GC が起きるようにリクエストをいっぱい送ります
devtools を開いてコンソールからこれを実行します
1000 回だと運が悪いと実行されないかもなので余裕をもたせて 10 倍です

for (const _ of Array(10000)) {
await fetch("")
}

並列しない 1 万回リクエストでも 30 秒ほどで終わりました
セッションフォルダを見てみると

# ll /var/lib/php/session/
total 4
-rw------- 1 apache apache 14 Jan 29 15:07 sess_6jbr0nb9emapvlknsbpsv2ojgv

古いほうが消えています

ブラウザでもアクセスしてみると カウントが 0 に戻っていました
運が悪いと 30 分も持たずにセッションデータは消えるようです

ランダム性はトラブルのもとな気がするので 個人的には固定の経過時間とセッションファイル数で GC してくれる方が好みなんですけどね
とは言っても cron 等に頼らず PHP だけでやるならリクエストごとに GC をするしないの判断しかできませんし 前回の実行時間やリクエスト回数を使うなら外部に保存することになってファイルの読み書きが発生しますし 今のが一番シンプルで効率良い方法なのでしょう