ホーム>Laravel>Laravel 419エラー解決早見表
Laravel

Laravel 419エラー解決早見表

いつもご利用ありがとうございます。
この記事には広告が掲載されており、その広告費によって運営しています。

Laravelの419エラーの解決早見表です。確認ポイントを紹介します。

関連動画

質問やフィードバック

この記事や動画に関する質問やフィードバックあれば、動画のコメント欄にてお気軽にコメントしてください。

Laravel 419 エラー解決早見表

419 PAGE EXPIREDが出たら、まず送信方法から確認してください。

詳細については、後述される見出しを読んでみてください!

症状・状況チェックすべきこと対処法・確認の進め方
Blade のフォームで POST するとき送信する form に@csrfがあるか、_tokenが送信されているかform 内に@csrfを追加し、ページを開き直して送信
Laravel 内の Ajax・fetch・jQuery・Axios で POST するときmeta タグとX-CSRF-TOKENヘッダーmeta タグから取得したトークンをヘッダーに付与
Sanctum + Axios の SPA のときCSRF 初期化、XSRF-TOKEN Cookie、X-XSRF-TOKENヘッダー/sanctum/csrf-cookieの取得後にログインなどを実行
フロントエンドと API がサブドメインで分かれているときCORS と Axios の Cookie 送信設定supports_credentialswithCredentialswithXSRFTokenを確認
API が完全に別ドメインにあるサブドメイン構成と混同していないかAPI トークンでやる
CSRF とセッションの関係を切り分けたいVerifyCsrfTokenStartSession下記の実験結果を参照し、保護を外すことを解決策にしない
@csrfを書いたのにフォーム送信で 419 になるform の外に置いていないか、検索フォームにもあるか送信対象の form 内へ配置する方法が紹介されている
長時間放置した画面や複数タブで 419 になるセッションの期限、別タブでのログイン・ログアウトページ再取得や再ログイン、SESSION_LIFETIMEの確認が候補
設定変更後も 419 が続く設定キャッシュなどが残っていないかconfig:clearcache:clearが候補
Cookie が保存されず、設定ファイルを追加・編集しているPHP 設定ファイルの先頭に空行がないか<?phpを 1 行目に直した事例あり
トークンがあってもセッションが継続しないStartSessionの適用、Cookie の属性、Sanctum の設定ルートのミドルウェアと Cookie の保存・送信を確認する方法が候補
ロードバランサー・複数サーバー構成で 419 になるサーバー間でセッションを共有しているかRedis など共通の保存先を使う方法が候補
419 発生時の案内を変えたい例外処理の場所と Laravel のバージョンTokenMismatchExceptionの捕捉などが紹介されている
外部サービスからの Webhook で CSRF 検証に失敗するブラウザのフォームと同じ保護方式が適切か特定ルートの除外と送信元の署名検証を検討

解決方法

1. Blade のフォームでは form 内に@csrf を追加して解決しました

フォームで、@csrfを付けずに POST すると、次のエラーになりました。

419 PAGE EXPIRED

送信する form の中に@csrfを追加して解決しました。

<form method="POST" action="{{ route('post') }}">
  @csrf
  <input name="post" />
  <button type="submit">post</button>
</form>

@csrfからは、次のような hidden の input タグが生成されます。

<input
  type="hidden"
  name="_token"
  value="vond93ovKGBBXpALpxAu4Ka9V646MW8tm9BvLRFp"
/>

確認するときは、ブラウザの検証を使って、

  1. HTML にname="_token"があること
  2. 開発者ツールの Network で送信データに_tokenが含まれていること

を見ると良いです。

2. Laravel 内の Ajax では meta タグのトークンをヘッダーに付けて解決しました

form を使わない非同期通信では、meta タグに埋め込んだトークンをX-CSRF-TOKENヘッダーに付与する方法で対応しました。

まず、Blade で描画するページの<head>内に、次の meta タグを追加しました。

(以降の JavaScript,jQuery,Vue すべてに以下のコードが必要です)

<head>
  <!-- ほかのタグ -->
  <meta name="csrf-token" content="{{ csrf_token() }}" />
</head>

JavaScript(fetch) の場合

document.querySelectorで meta タグからトークンを取得し、fetch のヘッダーに付与します。

function post() {
  const url = "/post"
  fetch(url, {
    method: "POST",
    headers: {
      "X-CSRF-TOKEN": document
        .querySelector('meta[name="csrf-token"]')
        .getAttribute("content"),
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      // 送信するデータ
    }),
  })
    .then(res => {})
    .catch(err => console.log(err))
}

jQuery を読み込んでいないページでも、このコードだけで動きます。

jQuery(ajax) の場合

jQuery の$.ajaxでも、同じ meta タグから取得したトークンを付与すれば OK です。

let url = "/post"
$.ajax({
  headers: {
    "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content"),
  },
  url: url,
  type: "POST",
  data: {
    // 送信するデータ
  },
})
  .done(function () {})
  .fail(function (data) {})

Vue などで使う Axios の場合

Axios では毎回ヘッダーを書く手間を省くため、resources/js/bootstrap.jsでまとめて設定しました。

window.axios = require("axios")
window.axios.defaults.headers.common = {
  "X-Requested-With": "XMLHttpRequest",
  "X-CSRF-TOKEN": document
    .querySelector('meta[name="csrf-token"]')
    .getAttribute("content"),
}

いずれの方法でも、確認するポイントは送信リクエストのX-CSRF-TOKENが meta タグの値と一致しているかです。

3. Sanctum + Axios では Cookie 方式で解決しました

Laravel プロジェクト外の JavaScript フレームワークから Sanctum + Axios で通信する構成では、

axios.get("/sanctum/csrf-cookie").then(response => {
  // ログイン処理…
})

上記のコードを、アプリが初期化される最上流のファイル(例: resources/js/app.js や main.js、Vue なら createApp().mount()する前)に書いてください。

別プロジェクトをサブドメインで運用する場合

config/cors.phpで Cookie を伴う通信を許可すれば OK です。

'supports_credentials' => true,

resources/js/bootstrap.jsでは、Axios に次の設定を追加します。

axios.defaults.withCredentials = true
axios.defaults.withXSRFToken = true

Laravel 11.x Sanctum の日本語ドキュメントです。

4. 完全に別ドメインの場合は、API トークンで

サブドメインでの構成では上記の方法を使えましたが、完全に別ドメインを API サーバーとして使ったときは、上記の方法では Cookie をセットできませんでした。

なので、API トークンを使った実装でやります。

トークンをバックエンドから送信する API を作ります。

$token = $user->createToken('frontend')->plainTextToken;
return response()->json([
    'token' => $token,
]);

API から受け取った token を、localStorage 等に保存しておきます。

axios で送信する時の headers にこんな感じで token を付与してあげれば OK です。

await axios.get("https://backend-api.com/api/user", {
  headers: {
    Authorization: `Bearer ${token}`,
    Accept: "application/json",
  },
})

トークンの保存場所や保有時間などセキュリティ設定が難しい構成です。

特に理由がなければ同一ドメイン(サブドメイン)運用が楽ですね。

よくあるミス

@csrf が form の外にある・検索フォームにない

確認時は、ページ内のどこかに@csrfがあるかだけでなく、実際に送信する form の中にあるかを見てください。

セッション期限切れ・複数タブでの状態の変化

確認時は、config/session.phplifetimeSESSION_LIFETIME、放置時間、別タブでのログイン・ログアウトの順序を記録してください。

入力内容を控えたうえでページを開き直す、必要なら再ログインして送信する、といった切り分けが候補になるようです。

設定変更が反映されない・キャッシュが残っている

config/cors.php などを編集した結果が反映されてなかったりするパターンがあるので、

php artisan cache:clear
php artisan config:clear

を実行してみましょう。

ロードバランサー環境でセッションを共有していない

バックエンドサーバーが複数あり、ロードバランサーにて振り分けている場合はセッションをサーバー間で共有する仕組みにしなければなりません。

自分は、こういう時 Redis でセッション管理すると楽なので使います。

Laravel では、セッションで Redis を使う時以下の.envの項目を触れば OK です。

SESSION_DRIVER=redis
SESSION_CONNECTION=default
SESSION_LIFETIME=120

REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
REDIS_DB=0

Laravel の公式資料でも、複数の Web サーバーでは Redis やデータベースなどの共通保存先を選ぶことが勧められているようです(公式 Session 資料)。

私自身は試していません。

仕組みの説明

Middleware にて、CSRF 保護とセッションの関係を定義してあります。

当時の構成では、routes/web.phpに適用されるwebミドルウェアグループに CSRF 保護が設定されています。

試しに、その2つの設定を以下のようにコメントアウトしてみます。

protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        // \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        // \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
];

フォームからも@csrfを取りました。

<form method="POST" action="{{ route('post') }}">
  <input name="post" />
  <button type="submit">post</button>
</form>

この状態で POST すると、419 エラーが発生しません。

VerifyCsrfTokenを外すことで CSRF 保護が動かなくなったということです。

また、StartSessionの Middleware を外すと、CSRF トークンが生成されなくなりました。

419 エラーを消すために、これらのミドルウェアを外すことは勧めません。

この仕組みについては、Laravel 10 以前の構成で検証した記録となり、Laravel11 以降では、別のところに書いてあるみたいですが、まだ検証してません。

フィードバックのお願い
この記事のフィードバックがありましたらYoutubeの適当な動画にコメントしていただいたり、お問い合わせからご連絡ください。