― メインドメイン × サブドメイン × セッション競合という地雷 ―
起きたこと(概要)
Xserverの環境で、
PHPセッション自体は動いているのに、ページ遷移やPOSTを跨ぐと session_id() が毎回変わる
という不可解な現象に遭遇しました。
- ローカル環境では正常
- セッションファイルも保存されている
- Cookieも送信されている
- それでもセッションが維持されない
原因が見えづらく、かなり時間を取られたトラブルです。
環境の特徴(伏せ字)
- メインサイト:
example.org - サブドメイン:
app.example.org - 両方とも同一レンタルサーバー上
- PHPの標準セッション(ファイル保存)
session.use_strict_mode = 1
一見すると「サブドメインはメインの配下にあるだけ」に見えますが、
Cookieとセッションの世界では、これは完全に別アプリ扱いになります。
症状
- セッションは開始される
- セッションファイル(
sess_xxx)も作成される - しかしPOST時や次ページで 別の
session_idが発行される - CSRFトークンやログイン判定が常に失敗する
デバッグしてみると、リクエストヘッダにこんな状態が出ていました。
Cookie: PHPSESSID=aaaa...; PHPSESSID=bbbb...
同じ名前の Cookie が2つ送られている。
真の原因
1. メインとサブで「同じセッション名」を使っていた
- メインサイト:
PHPSESSID - サブドメイン:
PHPSESSID
Domain / Path / 設定差により、
同名のセッションCookieが複数発行されて共存していました。
2. ブラウザは両方送る
ブラウザはルール通り、条件に合うCookieをすべて送信します。
PHP側はその中から1つを拾いますが、どれを拾うかは安定しません。
3. strict_mode がトドメを刺す
session.use_strict_mode=1 の場合、
- 渡されたIDに対応するセッションファイルが見つからない
- → 不正ID扱い
- → 新しい session_id を再発行
結果として
「セッションは動くが、維持されない」 という地獄が完成します。
なぜローカルでは再現しない?
- ドメインが単純
- Cookieが1枚しか存在しない
- レンタルサーバー特有の save_path / Cookie挙動がない
つまり、本番特有の事故でした。
解決策(最短・確実)
サブドメインを「完全に別アプリ」として扱う
① セッション名を分ける
session_name('APPSESSID'); // サブドメイン専用
② Cookieをホスト限定にする
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'domain' => '', // ホスト限定
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
③ save_path は必ず明示指定
レンタルサーバーでは
phpinfo()の表示を信用しない。
ini_set('session.save_path', '/full/path/to/session');
④ 過去の Cookie を全削除
- メインドメイン
- サブドメイン
- 親ドメイン共有分
これをやらないと「亡霊Cookie」で再発します。
学び(重要)
- サブドメインは別世界「のつもり」で設計すると痛い目を見る
- セッションを共有しないなら、名前を分けるのが最強
- Cookieが2枚来ていたら、PHPの挙動は信用できない
- レンタルサーバーでは
「表示されている設定」と「実体」が違うことがある
まとめ
この手のトラブルは、
- PHPの仕様
- ブラウザのCookie仕様
- レンタルサーバー独自挙動
この3点をまたがないと原因に辿り着けません。
正直かなりやっかいですが、
一度理解すれば再発は防げるタイプの地雷でもあります。


