LaravelでTokenMismatchExceptionが発生したときの対処方法
- 作成日 2026.03.17
- その他
LaravelのTokenMismatchExceptionは、CSRFトークンの不一致によって発生する代表的な例外の1つ。特にPOST、PUT、PATCH、DELETEのような状態変更系リクエストで起こりやすく、フォーム送信、Ajax通信、ログイン画面、管理画面、外部Webhook受信時などで頻出する。見た目としては419 Page Expiredや500系のように見えることもあるが、実際には「送信されたトークン」と「セッションに保存されているトークン」が一致していないことが原因である場合が多い。対処を速くするには、フォーム・セッション・Cookie・JavaScript送信ヘッダ・除外ルートの5つに分けて確認すると整理しやすい。
TokenMismatchExceptionとは何か
LaravelはCSRF対策として、ユーザーごとのセッションにトークンを保持し、フォームやAjaxから送られたトークンと照合している。これが一致しない場合に、リクエストは拒否される。
つまりTokenMismatchExceptionは「不正アクセス」だけでなく、「正しいユーザーなのにトークンの渡し方が崩れている」場合にも発生する。
このエラーが発生する代表的な条件
発生しやすい条件は次の通り。
・フォームに @csrf を入れていない
・JavaScriptのPOST送信でCSRFヘッダを付けていない
・セッションが切れているのに古い画面を送信した
・複数タブを長時間開いたまま送信した
・ログイン直後やセッション再生成後に古いトークンを送った
・CookieドメインやSameSite設定がずれている
・Webhookのように外部サービスから送られるリクエストにCSRF保護が掛かっている
まず最初に確認すること
最初に見るべきポイントは次の3つ。
・対象リクエストは web ミドルウェア配下か
・フォームまたはJavaScriptでトークンを送っているか
・セッション/Cookie が正しく維持されているか
LaravelのCSRF検証は通常 web グループ側で動くため、APIルートとWebルートが混ざっていると切り分けを誤りやすい。
フォーム送信での原因:@csrf が入っていない
最も多い原因は、BladeフォームにCSRFトークンを埋め込んでいないこと。
Laravelではフォームの中に @csrf を書くことで、hiddenの _token フィールドが自動生成される。
<form method="POST" action="/profile">
@csrf
<input type="text" name="name">
<button type="submit">保存</button>
</form>これが無いと、POST送信時に高確率でTokenMismatchExceptionになる。
手書きHTMLや古いテンプレートを流用したときに起きやすい。
Ajax・fetch・Axiosでの原因:ヘッダにトークンが無い
JavaScriptから送信する場合、フォームの hidden _token は使われないため、ヘッダでトークンを送る必要がある。
典型的には X-CSRF-TOKEN または X-XSRF-TOKEN を使う。
<meta name="csrf-token" content="{{ csrf_token() }}">
fetch('/profile', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
},
body: JSON.stringify({
name: 'Taro'
})
});発生条件として多いのは、
・GETでは動くのにPOSTだけ落ちる
・Axios設定を消した
・フロントだけ別テンプレートでmetaタグを出していない
といったケース。
Axios利用時の基本設定
Axiosを使う場合は、全リクエストに自動でCSRFヘッダを付ける構成にすると事故が減る。
import axios from 'axios';
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
const token = document.querySelector('meta[name="csrf-token"]');
if (token) {
axios.defaults.headers.common['X-CSRF-TOKEN'] = token.getAttribute('content');
}これを共通の bootstrap.js などに置いておけば、画面ごとに個別設定する必要が減る。
セッション切れが原因になるケース
フォームに @csrf を入れていても、セッション自体が切れていればトークン照合は失敗する。
よくある状況は次の通り。
・長時間開いたフォームをあとで送信
・ログイン後に別端末や別ブラウザでセッションが変わった
・セッションドライバの設定不備で毎回新しいセッションになる
・Redisやfileセッションの保存が壊れている
この場合、フォーム側ではなくセッション維持の問題を疑うべき。
セッション設定の確認ポイント
セッション由来のTokenMismatchExceptionでは、.env や config/session.php の設定も影響する。
よく見るべき項目は以下。
・SESSION_DRIVER
・SESSION_DOMAIN
・SESSION_SECURE_COOKIE
・same_site
・ロードバランサ配下ならHTTPS判定
特に、HTTP/HTTPSの混在やサブドメイン運用ではCookieが想定通り送られず、CSRFトークン不一致になりやすい。
# .env の例
SESSION_DRIVER=file
SESSION_DOMAIN=.example.com
SESSION_SECURE_COOKIE=true発生条件としては、
・ローカルでは動くのに本番だけ落ちる
・wwwあり/なしで挙動が違う
・管理画面だけ別サブドメイン
などが典型。
ログイン画面や認証周りで起きるケース
ログインやログアウト直後は、Laravelがセッションを再生成するため、古いフォームや古いトークンが残っていると不一致になりやすい。
たとえば次のようなケース。
・ログインページを開いたまま長時間放置して送信
・別タブで再ログイン後、古いタブからPOST
・CSRFトークンをJavaScript変数に保持していて更新していない
この場合は、ページリロードで直ることが多いが、根本的には「古いトークンを送らせない」設計が必要。
Webhookや外部連携で起きるケース
StripeやSlackなどの外部サービスからのWebhookには、LaravelのCSRFトークンは送られない。
それなのに web ミドルウェア配下に置いていると、必ずTokenMismatchExceptionになる。
この種のルートは、CSRF検証から外す必要がある。
Laravelのバージョンによって設定場所は異なるが、考え方としては「外部から来る正当なコールバックはCSRF保護対象から除外する」。
ただし、何でも除外すると危険なので、Webhook等の必要最小限に限定する。
// 例:Webhookは専用ルートに分ける考え方
Route::post('/webhooks/stripe', [StripeWebhookController::class, 'handle']);
発生条件:
・決済完了後だけ419/TokenMismatchException
・外部サービスのコールバックが届いているのにアプリ側で拒否
・テストツール(Postman等)からPOSTすると落ちる
SPAやフロント分離構成での原因
SPA構成では、CSRFトークンとセッションCookieの両方が揃わないと失敗する。
起きやすい条件は次の通り。
・フロントとAPIでドメインが違う
・Cookieが送信されていない
・CSRF Cookieの取得をしていない
・withCredentials が無い
・CORS設定だけ整えてCSRF側を忘れている
SPAでは「トークンヘッダだけ送ればいい」ではなく、Cookieベース認証ならセッションも成立している必要がある。
設定変更後に直らない場合
Laravelでは設定キャッシュの影響で、.env 修正後も古い設定を読んでいることがある。
セッションやCookie設定を変えたのに改善しない場合は、キャッシュクリアを確認する。
php artisan config:clear
php artisan cache:clear
php artisan route:clear
php artisan view:clear必要ならまとめて消す。
php artisan optimize:clear発生条件:
・.env を修正したのに本番挙動が変わらない
・セッションドライバ変更後もエラー継続
・Docker再起動後だけ直る
対処の優先順位
TokenMismatchExceptionが出たら、次の順で切り分けると速い。
- フォームに
@csrfがあるか - Ajaxなら
X-CSRF-TOKENを送っているか - セッションが維持されているか
- Cookieドメイン・HTTPS・SameSite が合っているか
- 外部Webhookを誤ってCSRF対象にしていないか
- 設定キャッシュが古くないか
この順で見ると、だいたいの原因にたどり着ける。
よくある誤った対処
やってはいけない対応も多い。
・CSRF保護を全部無効化する
・原因不明のまま全POSTルートを除外する
・本番でAPP_DEBUG=trueにして調査する
・セッション設定を場当たり的に変え続ける
TokenMismatchExceptionは「セキュリティ機構が働いている結果」でもあるため、雑に無効化するのではなく、正しく通す方向で直すべき。
まとめ
LaravelのTokenMismatchExceptionは、ほとんどの場合「CSRFトークンの送信漏れ」か「セッション/Cookie不整合」で起きる。
対処の基本は、
・フォームには @csrf を入れる
・Ajaxでは X-CSRF-TOKEN を送る
・セッション設定を確認する
・Webhookは必要な範囲だけ除外する
・設定変更後はキャッシュを見直す
この流れを押さえること。
単に例外を消すのではなく、「なぜトークンが一致しなかったのか」を切り分けることが、再発防止にもつながる。
-
前の記事
Laravelでdump()とdd()を使う方法 2026.03.13
-
次の記事
Laravel Telescopeを使った開発中のデバッグとモニタリング 2026.03.18
コメントを書く