Laravel 5.2 Ajax POST TokenMismatchException with a valid CSRF token


I'm having trouble with an AJAX POST request in Laravel 5.2. I'm passing a valid CSRF token in my AJAX request.

Opening and closing the form using Laravel Collective HTML package form tags, which automatically adds a hidden _token input with the CSRF token. Rendered HTML:

<form method="POST" action="" accept-charset="UTF-8" id="product-search-form">
<input name="_token" type="hidden" value="mOaBxU1sVUsRX1KkuAeVhSDSxj0LKT8DDxl9USQc">

    <label for="keywords" class="required">Keywords</label>
    <input id="keywords" placeholder="Enter keywords" name="keywords" type="text">

    <label for="category" class="required">Category</label>
    <select id="category" name="category">
        <option selected="selected" value="">Choose a category...</option>
        <option value="category-1">Category 1</option>
        <option value="category-2">Category 2</option>
        <option value="category-3">Category 3</option>

    <button id="search-product-submit" type="submit">Search</button>


Using Fetch API and FormData to do AJAX request:

function $(id) {
    return document.getElementById(id);

var searchProductSubmitButton = $("search-product-submit");

function fetchStatus(response) {
    if (response.status >= 200 && response.status < 300) {
        return Promise.resolve(response);
    } else {
        return Promise.reject(new Error(response.statusText));

function fetchJson(response) {
    return response.json();

function searchProducts(evt){
    var keywords = $("keywords").value,
        categorySelect = $("category"),
        category = categorySelect.options[categorySelect.selectedIndex].value,
        csrfToken = document.getElementsByTagName("meta")["csrf-token"].getAttribute("content"),
        productSearchForm = $("product-search-form"),
        formData = new FormData(productSearchForm);


    if(keywords !== "" && category !== ""){
        fetch("/admin/products/search", {
            method: "POST",
            body: formData,
            headers: {
                "X-CSRF-TOKEN": csrfToken
        .then(function(products) {
            console.log("The operation was a complete success");
        }).catch(function(error) {
            console.log("Request failed", error);
searchProductSubmitButton.addEventListener("click", searchProducts, false);
searchProductSubmitButton.addEventListener("keypress", searchProducts, false);

The CSRF token and form data are in the request payload:

Content-Disposition: form-data; name="_token"

Content-Disposition: form-data; name="keywords"

Content-Disposition: form-data; name="category"


The _token input in the request payload and the x-csrf-token header are the same as the token seen in View Source.

Route is using web middleware:

Route::group(['prefix' => 'admin', 'middleware' => 'web'], function () {
    Route::post('products/search', [ // == "admin/products/search" with prefix
        'as'   => '',
        'uses' => '[email protected]'


public function search(SearchRequest $request){
    Log::info('keywords: ' . $request->keywords);
    Log::info('category: ' . $request->category);

    return response()->json([
        'keywords' => $request->keywords,
        'category' => $request->category
    ], 200);

I had these problems sometimes too and fixed it by sending a POST with _token instead of putting it in the headers.

Or why don't you serialize the whole form?