Play Frameworkでセッションが消える?エラー原因と解決方法を徹底解説
生徒
「Play Frameworkでログイン機能を作っているのですが、ページを移動するとセッションが消えてしまったり、エラーが出たりして困っています。」
先生
「セッション関連のトラブルですね。Play Frameworkのセッションはクッキーを使っているため、クッキーのサイズ制限やセキュリティ設定、シークレットキーの不一致など、いくつか特有の原因が考えられます。」
生徒
「具体的にどこを確認すれば解決できるのでしょうか?」
先生
「初心者がハマりやすいエラーパターンとその対策を順番に見ていきましょう!」
1. クッキーのサイズ制限によるデータ消失
Play Frameworkのセッション管理において、最も多いトラブルの一つが「クッキーのサイズ上限」です。 一般的なブラウザでは、一つのクッキーに保存できるデータ量は約4キロバイトまでに制限されています。 セッションに大量のユーザー情報や、大きなオブジェクトを詰め込もうとすると、この上限を超えてしまいます。
上限を超えた場合、ブラウザはクッキーを保存しなくなるため、次のリクエストではセッションが空の状態になってしまいます。 これが「ページを移動するとログアウトされる」原因の多くです。 解決策としては、セッションにはユーザーIDなどの最小限の識別子だけを保存し、詳細なデータはデータベースから取得するように設計を変更することです。
2. APPLICATION_SECRETの不一致による署名エラー
Play Frameworkは、クッキーの改ざんを防ぐためにシークレットキーを使用して署名を行っています。
もし、アプリケーションの再起動時やデプロイ時にこの play.http.secret.key が変更されてしまうと、それ以前に発行されたセッションクッキーはすべて無効になります。
開発環境で sbt run を再実行するたびにセッションが切れる場合は、設定ファイルで固定のキーが指定されているか確認しましょう。
本番環境では、複数のサーバーで同じキーを共有していないと、サーバーをまたぐたびにセッションエラーが発生してしまいます。
# conf/application.conf の設定例
# キーが未設定だと起動時にランダム生成されるため、必ず固定の値を設定する
play.http.secret.key = "changeme-for-production-security-key-12345"
3. HTTPSとSecure属性の設定ミス
セキュリティを意識して play.http.session.secure = true に設定している場合、初心者が陥りやすい罠があります。
この属性が有効なクッキーは、HTTPS(暗号化通信)を通じてのみ送信されます。
もし開発環境が HTTP(http://localhost:9000)のままだと、ブラウザはクッキーをサーバーに送り返しません。
その結果、ログイン処理は成功しているのに、その後のページではセッションが取得できないという現象が起きます。
開発環境ではこの設定を false にするか、開発環境自体を自己署名証明書などでHTTPS化する必要があります。
package controllers;
import play.mvc.*;
import com.typesafe.config.Config;
import javax.inject.Inject;
public class SecurityCheckController extends Controller {
@Inject Config config;
public Result checkSecureSetting() {
// 設定値がtrueかつHTTP接続だと、セッションは機能しません
boolean isSecure = config.getBoolean("play.http.session.secure");
return ok("Secure属性の設定値: " + isSecure);
}
}
4. 異なるドメインやパスでのクッキー共有失敗
クッキーには「ドメイン」と「パス」という属性があります。
例えば、アプリを /app1 と /app2 のように異なるパスで運用している場合、デフォルト設定ではクッキーが共有されないことがあります。
また、サブドメイン(test.example.com と www.example.com など)をまたいでセッションを維持したい場合は、適切なドメイン設定が必要です。 これが正しく設定されていないと、ログインしたはずなのに特定のページにアクセスした途端にセッションが読み込めなくなります。
# サブドメイン間で共有する場合の設定例
play.http.session.domain = ".example.com"
5. ブラウザのSameSite属性による制約
近年のWebブラウザは、プライバシー保護のために SameSite 属性の扱いが厳しくなっています。
Play Frameworkのデフォルト設定である Lax は比較的安全ですが、外部サイトからリダイレクトで戻ってくるような決済連携などの処理では、クッキーが送信されずセッションが切れてしまうことがあります。
このエラーを防ぐには、連携の仕組みに合わせて属性を調整する必要があります。
ただし、安易に None に設定するとセキュリティリスクが高まるため、必要最低限の変更にとどめるのがプロの鉄則です。
6. セッションタイムアウトと有効期限切れ
セッションには必ず有効期限があります。
設定ファイルで play.http.session.maxAge が短すぎると、ユーザーが操作している最中にセッションが切れてエラーになります。
また、ブラウザを閉じると消える「一時セッション」なのか、ブラウザを閉じても残る「永続セッション」なのかを明確に使い分ける必要があります。 意図せずログアウトされてしまう場合は、この時間設定がミリ秒単位などで正しく記述されているか再確認しましょう。
package controllers;
import play.mvc.*;
import java.time.Duration;
public class SessionTimeoutController extends Controller {
public Result setDuration(Http.Request request) {
// 特定のリクエストで有効期限を意識的に上書きする場合のイメージ
return ok("セッション期限を意識しましょう")
.addingToSession(request, "last_access", String.valueOf(System.currentTimeMillis()));
}
}
7. 開発時のキャッシュと古いクッキーの干渉
プログラムを修正したのに、ブラウザに古いクッキーが残っているせいで、新しいセッション管理ロジックが正しく動作しないことがあります。 特にシークレットキーを変更した直後などは、ブラウザ側に残っている古い署名のクッキーが原因でエラーを誘発することがあります。
解決策はシンプルです。ブラウザのデベロッパーツールを開き、クッキーを一度すべて削除してから動作確認を行ってください。 初心者はコードのミスを疑いがちですが、実は「ブラウザのキャッシュや古いクッキー」が犯人であることも少なくありません。
8. セッションデータの型変換とNullPointerException
Play Frameworkのセッションは、値をすべて「文字列(String)」として保存します。 数値を保存したつもりでも、取り出すときは文字列として返ってくるため、Java側で数値への変換処理を忘れると実行時エラーが発生します。
また、セッションから値を取得する際は、必ず Optional を使って値が存在しない場合(Null)を考慮したコードを書くことが、エラーでアプリを落とさないための重要なポイントです。
package controllers;
import play.mvc.*;
import java.util.Optional;
public class ConversionController extends Controller {
public Result getAge(Http.Request request) {
Optional<String> ageStr = request.session().get("user_age");
// 文字列から数値への変換時は例外処理を忘れずに!
try {
int age = ageStr.map(Integer::parseInt).orElse(0);
return ok("年齢は " + age + " 歳です");
} catch (NumberFormatException e) {
return badRequest("セッションデータが不正です");
}
}
}
9. 認証フィルター(Action)での無限ループ
ログインチェックを共通化するために「アクション合成(Action Composition)」を使っている場合、リダイレクト先の設定ミスで無限ループが発生することがあります。 「未ログインならログイン画面へ飛ばす」という処理を、ログイン画面自体にも適用してしまうと、ログイン画面にアクセスするたびに自分自身にリダイレクトし続けてしまいます。
ブラウザに「リダイレクトが多すぎます」といったエラーが表示された場合は、認証チェックからログインページや静的ファイルを除外できているか、ロジックを再点検しましょう。