10.

Laravel「The page has expired due to inactivity」の原因と対処(CSRF / セッション切れ)

編集
この記事の要点
  • Laravel の CSRF トークン切れが原因。フォーム表示から送信までの間にセッションが失効
  • 対処1: フォームに @csrf を入れる(POST フォーム必須)
  • 対処2: config/session.phplifetime を延長(デフォルト 120 分)
  • 対処3: 長時間フォームには Ajax で CSRF トークン再取得を実装
  • 対処4: SESSION_DRIVER がファイルだと storage 権限不足で セッションが書けず即失効する → Redis / DB に

エラー画面

419 | Page Expired
The page has expired due to inactivity.
Please refresh and try again.

Laravel のCSRF (Cross-Site Request Forgery) 保護機構が、リクエストの _token とサーバ側セッションの _token が一致しないと判断したときに表示します。HTTP ステータスは 419 (Authentication Timeout)

原因と対処の早見表

原因確認対処
フォームに @csrf 無しform タグ内を確認@csrf 追加
セッション失効表示後の経過時間SESSION_LIFETIME 延長
storage/framework/sessions 権限不足ls -la storage/framework/sessions権限修正 or Redis 化
SESSION_DOMAIN ミス.env の値ドメイン合わせる
HTTPS 化したのに SESSION_SECURE_COOKIE 未設定.env / Chrome DevTools のCookiesSESSION_SECURE_COOKIE=true
マルチドメインで Cookie 共有失敗サブドメイン構成SESSION_DOMAIN=.example.com

対処1: フォームに @csrf を追加

POST / PUT / PATCH / DELETE フォームには必ず @csrf を入れます:

{{-- ❌ NG: トークン無し → 419 --}}
{{-- ✅ OK --}}
@csrf
{{-- @csrf の展開後 --}}

Ajax / fetch の場合

{{-- HTML  に --}}


対処2: セッション寿命を延長

// config/session.php
'lifetime' => env('SESSION_LIFETIME', 120),   // 分単位、デフォルト 120 分
'expire_on_close' => false,                   // ブラウザ閉じても保持
# .env
SESSION_LIFETIME=480    # 8 時間

長時間入力フォーム(記事作成画面など)には定期的に CSRF トークンを再取得するのが本筋:

// 10 分おきにトークンをリフレッシュ
setInterval(async () => {
    const res = await fetch('/csrf-token');  // 専用エンドポイント
    const data = await res.json();
    document.querySelector('meta[name="csrf-token"]').content = data.token;
    document.querySelector('input[name="_token"]').value = data.token;
}, 600 * 1000);
// routes/web.php
Route::get('/csrf-token', fn() => response()->json(['token' => csrf_token()]));

対処3: storage 権限の確認

セッションドライバが file で storage が書けないと、毎リクエスト新セッションになり即 419:

# 確認
ls -la storage/framework/sessions
# drwxr-xr-x  www-data:www-data の存在を確認

# 修正
chown -R www-data:www-data storage bootstrap/cache
chmod -R 775 storage bootstrap/cache

# Laravel Sail
./vendor/bin/sail artisan storage:link

セッションドライバを Redis / DB へ

# .env
SESSION_DRIVER=redis        # おすすめ(高速 + クラスタ対応)
SESSION_DRIVER=database     # 代替
SESSION_DRIVER=cookie       # 暗号化 Cookie に格納(容量制限あり)
# DB ドライバ使用時
php artisan session:table
php artisan migrate

対処4: HTTPS / マルチドメインでの Cookie 設定

# .env

# HTTPS のみ Cookie 送信(本番では必須)
SESSION_SECURE_COOKIE=true

# サブドメイン間で Cookie 共有
SESSION_DOMAIN=.example.com

# SameSite 制御
SESSION_SAME_SITE=lax       # lax / strict / none
# none を使う場合は SECURE_COOKIE=true 必須

対処5: 特定の URL を CSRF 検証から除外

外部 Webhook 受信 / 決済リダイレクト等、CSRF トークンを付けられない POST がある場合:

// app/Http/Middleware/VerifyCsrfToken.php
class VerifyCsrfToken extends Middleware
{
    protected $except = [
        '/webhook/stripe',
        '/webhook/*',
        '/payment/notify',
    ];
}

注意: 除外は最小限に。外部 API の署名検証などで別途守る必要があります。

トラブルシュート手順

  1. Chrome DevTools → Application → Cookies で XSRF-TOKENlaravel_session が存在するか
  2. 送信時の Form Data に _token が含まれているか確認
  3. storage/logs/laravel.log に詳細エラーが出ていないか
  4. storage/framework/sessions/ 直下にファイルが書かれるか(file ドライバの場合)
  5. クリックして送信するまでの滞在時間が SESSION_LIFETIME を超えていないか

FAQ

Q: ブラウザを開きっぱなしで翌日送信したら 419
A: 期待動作です。長時間の場合は Ajax で定期的にトークン更新するか、SESSION_LIFETIME を延長してください。

Q: ログイン後すぐ 419 になる
A: SESSION_DRIVER=file + storage 権限不足のパターンが多いです。Redis / DB ドライバを推奨。

Q: SPA で 419 が出る
A: Laravel Sanctum を使う場合は config/sanctum.phpstateful ドメイン設定と、フロントの withCredentials: true を確認。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. SQLSTATE[HY000] [1045] Access denied for user 'homestead'@'localhost'
  2. Add [~] to fillable property to allow mass assignment on [App\~].
  3. PHP Parse error: syntax error, unexpected 'class' (T_CLASS), expecting identifier (T_STRING) or variable (T_VARIABLE) or '{' or '$' in ~
  4. Changing columns for table "~" requires Doctrine DBAL; install "doctrine/dbal"
  5. MethodNotAllowedHttpException No message
  6. Class 'Doctrine\DBAL\Driver\PDOMySql\Driver' not found
  7. production.ERROR: No application encryption key has been specified.
  8. Dotenv values containing spaces must be surrounded by quotes.
  9. Laravel \ Socialite \ Two \ InvalidStateException
  10. The page has expired due to inactivity. Please refresh and try again.
  11. Failed to clone https://github.com/symfony/thanks.git via https, ssh protocol
  12. Illegal offset type
  13. Cannot access protected property Illuminate\Http\Request::$...
  14. Emitted value instead of an instance of Error
  15. 画像保存時にInternal Server Error
  16. Failed to authenticate on SMTP server with username ...
  17. PostTooLargeException
  18. Database hosts array is empty.
  19. Invalid request (Unsupported SSL request)
  20. does not comply with psr-4 autoloading standard. Skipping.
  21. MySQLのSTR_TO_DATE関数を使用するとnullが返却される問題