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

rappasoft's avatar

Laravel 5 AJAX TokenMismatchException

I have a page that starts about 20 AJAX requests on load, and loads the contents in.

I have a meta tag in the header called _token, and I have this as my ajaxSetup:

$.ajaxSetup({
                headers: {
                    'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content')
                }
            });

It's hit or miss, sometimes when I run the script all 20 requests finish without problem, other times, 15 may finish and 5 will fail or 10 will finish and 10 will fail. Each time different ones finish and fail. The logs fill up with TokenMistatchExceptions.

So I tried sending a fresh csrf_token() with each request, and same problem, the page source even shows they're all the same.

Is anyone else having issues? Or is there something i'm missing with L5?

0 likes
16 replies
rodrigo.pedra's avatar

Your approach is correct regarding the docs [ http://laravel.com/docs/5.0/routing#csrf-protection ]. One thing that crossed my mind is that maybe the $.ajaxSetup(...) is running after some of the requests are sent. Be sure to setup it before any app logic related script runs.

I mean:

    <script src="//code.jquery.com/jquery.min.js"></script>
    <script src="js/config.js"></script> <!-- Do your $.ajaxSetup in this file -->
    <script src="js/app.js"></script>

Also in your js files don't forget to wrap your code in a $(function () {...}); or in a $(document).ready(function () {...}) which have the same effect. This will assure that any jQuery script will run only after the DOM content is loaded, like so:

// public/js/config.js
$(function () {
    $.ajaxSetup({
        headers: { 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content') }
    });
});

If you are building your scripts with elixir, be sure to have your configuration code before your app code.

rappasoft's avatar

Attempted it, didn't make a difference. I'm gonna keep it like that anyway because its more correct. Could be be anything to do with the fact that i'm developing on localhost?

rappasoft's avatar

Speaking of elixir, are there ways to have different configurations for different pages?

rodrigo.pedra's avatar

Did you check the request headers in chrome/firefox dev tools to see if the header is being correctly set?

You could compile different scripts with elixir:

// ./gulpfile.js

elixir(function ( mix ) {
    mix.scripts([
        'vendor/bower_components/jquery/dist/jquery.min.js',
        'resources/assets/js/home/config.js',
        'resources/assets/js/home/app.js'
    ], 'public/js/home', './' );

    mix.scripts([
        'vendor/bower_components/jquery/dist/jquery.min.js',
        'resources/assets/js/other-page/config.js',
        'resources/assets/js/other-page/app.js'
    ], 'public/js/other-page', './' );
});

And then in your home use it like this:

<script src="{{ asset( 'js/home/all.js' ) }}"></script>

And then in your other-page use it like this:

<script src="{{ asset( 'js/other-page/all.js' ) }}"></script>
1 like
rodrigo.pedra's avatar

Regarding the token, try this:


    $.ajaxPrefilter(function(options, originalOptions, xhr) { // this will run before each request
        var token = $('meta[name="csrf-token"]').attr('content'); // or _token, whichever you are using

        if (token) {
            return xhr.setRequestHeader('X-CSRF-TOKEN', token); // adds directly to the XmlHttpRequest Object
        }
    });
rodrigo.pedra's avatar

Not actually, ajaxPrefilter will run before each request while ajaxSetup should set global configurations once.

But as you have described that sometimes the ajaxSetup is failing, give it a try.

1 like
constb's avatar

@rappasoft I think you make have older version of L5. X-CSRF-Token was not available in 5.0.0, it was added somewhere along the way. Try running composer update laravel/framework.

2 likes
philthathril's avatar

I had an issue on a different framework (cake I believe) where I was sending in a bunch of ajax requests at once and some were failing. It was random on when they would succeed and fail. I believe we increased the number of allowed connections that apache would receive at once, and that seemed to fix the problem. (It's been a minute, so I think that's the solution we came up with.)

Anyway, it may not be framework or javascript related - check your server configs too! Hope that helps.

jimmck's avatar

I just moved from L4 to L5 and this was giving fits. Till I got migrated I just disabled it in the framework.

<?php namespace Illuminate\Foundation\Http\Middleware;

use Closure;
use Illuminate\Contracts\Routing\Middleware;
use Symfony\Component\HttpFoundation\Cookie;
use Illuminate\Contracts\Encryption\Encrypter;
use Illuminate\Session\TokenMismatchException;
use Symfony\Component\Security\Core\Util\StringUtils;

class VerifyCsrfToken implements Middleware {

And then ion the function, I just returned true. Until I migrated the guts of my L4 code. And verified it worked.

/**
     * Determine if the session and input CSRF tokens match.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return bool
     */
    protected function tokensMatch($request)
    {
        $token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');

        if ( ! $token && $header = $request->header('X-XSRF-TOKEN'))
        {
            $token = $this->encrypter->decrypt($header);
        }

        return true;
        //return StringUtils::equals($request->session()->token(), $token);
    }

Not elegant, but I wanted to get stuff moved. I have since seen removing this from middleware stack as well. Anyway if you want to validate your update code.

jimmck's avatar

Once I got stuff migrated I went back to look at csrf issues.

I started by putting the token in my webpage view and picking it up in the constructor of my AJAX sender.

<!doctype html>
<html>
<head>
    <title>Manage Contact Types</title>
    <link rel="stylesheet" href="/assets/css/main.css">
    <link rel="stylesheet" href="/assets/css/alertify.css" />
    <link rel="stylesheet" href="/assets/css/themes/default.css" />
    <meta name="csrf-token" content="<?php echo csrf_token() ?>"/>
</head>

And then...

function PayloadMsg() {
    this.onPayload = function(){};
    this.onMsg = function(){};
    this.onErr =  function(){};
    this.onDef =  function(){};

    this.payLoad = "";
    this.async = true;
    this.busy = false;
    this.timeout = 60000;
    this.url_check_timeout = 10;    // should be higher over the web.

    this.urlOk = false;
    this.context = null;    // optional context object passed by user via call method.
    this.lastError = NetError.ok;

    //this.csrf_token = $('meta[name="csrf-token"]').attr('content');
    this.csrf_token = "";
    this.getToken();
    fyi("First Token [" + this.csrf_token + "]");

The Jquery call to get the token from header is commented out because I now make a get call to get the token from the server. I too noticed the token did not change all the time. And when I just let the webpage sit idle in the browser, eventually my token expires. Luckily when I post I first ping my router commands. When the fail I ask for a new token.

Post Call

PayloadMsg.prototype.call = function(url, callData, context) {
    this.urlExists(url, this.exists);

    if (this.urlOk == false || url == ""  || arguments.length == 0) {
        fyi("Bad URL [" + url + "]");
        this.getToken();
        return;
    }

This works good as I can recover from stale tokens. Its bad because anyone can make the get token call. I am considering mem cache and XORing the token. Or dropping the Session in mem cache. Any ideas or wisdom would be most welcome.

jimmck's avatar

When I make the call I always add the current token.

   // make sure if callData is an object we make it an empty object.
    // we do this so we can add _token to it.

    if (callData == "") {
        callData = {};
    }
    this.busy = true;
    callData['_token'] = this.csrf_token;
    fyi('Post token [' + callData['_token'] + ']');
    $.myPost(url, callData, this, this.handlePost, this.postError, this.timeout, this.async);
}
jimmck's avatar

The Get token exchange.

PayloadMsg.prototype.getToken = function () {
    $.ajax({
        type: 'GET',
        url: 'token',
        async: false,
        context: this,
        data: "",
        timeout: this.url_check_timeout,
        success: function (token) {
            this.csrf_token = token;
            fyi("Got new Token...[" + token + "]");
        }.bind(this),
        error: function () {
            this.lastError = NetError.invalidURL;
        }.bind(this)
    });
};

Route...

\Route::get('token', function () {
       return csrf_token();
   });
Vigikaran's avatar

The solution is adding domain parameter to session.php in the config

in .env DOMAIN=yourdomain.com and in config/session.php add

'domain' => env('DOMAIN', 'localhost'),

1 like

Please or to participate in this conversation.