Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

auxbus's avatar

How to investigate the rare 419 CSRF token error

I have a site with hundreds of daily users. 99% of the time, CSRF tokens works just fine. Very occasionally (not even once per day), I'll get a Illuminate\Session\TokenMismatchException CSRF token mismatch error. How can I debug this further, since it's entirely handled by Laravel? Is there a way to find out when a session was issued and when it expires? Sessions are one of those magical things and the docs don't mention it.

session.lifetime is set to 43800, so a month. I wasn't sure if the CSRF was related to that. So if it happens to expire while the user is on the application, they just have to reload the page? Seems like a strange way for Laravel to handle this.

Update since this is still an issue: I added some logging and when this does happen - which is rare - I confirmed that:

  • This only occurs on axios calls (ajax)!
  • The X-CSRF-TOKEN header doesn't match session()->token()
  • "By default, the resources/js/bootstrap.js file includes the Axios HTTP library which will automatically send the X-XSRF-TOKEN header for you." I confirmed this by printing window.axios.defaults.headers.common['X-CSRF-TOKEN'] to the console. It matched the value in <meta name="csrf-token" content="{{ csrf_token() }}"> (from the CSRF page)
  • The session is not expired. In fact, it was usually created semi-recently. In today's instance, about 41 hours prior. I have SESSION_LIFETIME set to 43800, about a month

So here's where I'm stuck. The header should match, and does most of the time (99%), but occasionally, it does not. I am unsure how to fix or debug this, as none of it involves my own code.

0 likes
10 replies
jlrdw's avatar

Yes of course session is related to csrf.

Token in session has to match forms submitted token. These are maybe expired tokens. You could setup a redirect.

The best practice is have users logout.

auxbus's avatar

@jlrdw Is there a way to find out when a session was issued and when it expires? I want to verify this is the issue.

auxbus's avatar

@Sinnbeck Is there a way to find out when a session was issued and when it expires? I want to verify this is the issue.

Sinnbeck's avatar

@auxbus Only using basic math I believe. As soon as you hit the backend, it is renewed, so you cannot check in php (as it will always be refreshed). So in theory you can add some javascript timer that counts down. The issue comes if the user closes the lid on their laptop or similar.

Yorki's avatar

Are you using SPA? If so you would need some mechanism to refresh token every x seconds/minutes. Anyway debugging this will be hard, you could try to put some session variable each time visitor refresh page on middleware, eg. timestamp and use logging on handle method of VerifyCsrfToken.php which is implemeting Illuminate\Foundation\Http\Middleware trait too see if the user just idled x seconds and token expired or its another issue.

auxbus's avatar

@Yorki Not a SPA. And that's my main question - how do you find out when a session was issued and when it expires?

Yorki's avatar

@auxbus simple answer is config session.lifetime determines the session duration which in the end is responsible for csrf token.

More complicated answer is that if you want the time which csrf token was issued it would need some digging into Illuminate/Session/Store.php and there is method called regenerateToken . You would need to override your session store driver and add to that method also creation date which you could later pull.

public function regenerateToken()
{
  $this->put('_token', Str::random(40));
  $this->put('_token_issued', time());
}
auxbus's avatar

I wanted to post an update here, since this is still an issue. I added some logging and when this does happen - which is rare - I confirmed that:

  • This only occurs on axios calls (ajax)!
  • The X-CSRF-TOKEN header doesn't match session()->token()
  • "By default, the resources/js/bootstrap.js file includes the Axios HTTP library which will automatically send the X-XSRF-TOKEN header for you." I confirmed this by printing window.axios.defaults.headers.common['X-CSRF-TOKEN'] to the console. It matched the value in <meta name="csrf-token" content="{{ csrf_token() }}"> (from the CSRF page)
  • The session is not expired. In fact, it was usually created semi-recently. In today's instance, about 41 hours prior. I have SESSION_LIFETIME set to 43800, about a month

So here's where I'm stuck. The header should match, and does most of the time (99%), but occasionally, it does not. I am unsure how to fix or debug this, as none of it involves my own code.

CC: @jlrdw @sinnbeck @yorki

Please or to participate in this conversation.