When I first separated my React.js front end from the Laravel 8 back end, I expected everything to work the same way as it did when React was embedded within Laravel’s resources
folder. The build worked, routes were fine, and even the interface loaded perfectly.
But the moment I tried to make a POST request using Axios, I hit a wall:
http://127.0.0.1:8000/userManagement/deleteCharity 419 (unknown status)
That’s when I realized I was dealing with a CSRF token mismatch a common Laravel security feature that doesn’t play well when React and Laravel are hosted separately.
First Code I Use
Here’s the code I was running in my React project to delete a charity record:
CSRF = await getCSRF(); // I was fetching the CSRF token from backend
let formData = new FormData();
formData.append("charity_id", Number(id));
formData.append("_method", 'patch');
try {
const response = await axios({
method: "post",
url: `${BaseURL}/userManagement/deleteCharity`,
data: formData,
headers: {
"Content-Type": "multipart/form-data",
"X-CSRF-TOKEN": CSRF.data, // using the fetched CSRF token
},
});
console.log(response);
console.log(response.data);
resetAll(); // some cleanup function
} catch (error) {
console.log(error);
}
This code worked perfectly in Postman, but kept throwing a 419 error in the browser.
Error 419 in Laravel
A 419 status code in Laravel means Page Expired. But in practice, it’s a CSRF Token Mismatch. Laravel expects a valid token with every state-changing request (POST, PATCH, DELETE, etc.).
React Is Outside Laravel
When React is served within Laravel (like from resources/js
), Laravel blade views inject the CSRF token directly into the HTML.
But in my case, React was running separately (like on http://localhost:3000
), so:
- Laravel couldn’t inject the CSRF token into the front-end.
- My React app didn’t automatically share the same session cookies.
- Even with a manually added
X-CSRF-TOKEN
, it wasn’t syncing the session correctly.
How I Fix It
Let Laravel Sanctum Handle CSRF
If you’re using Laravel Sanctum, here’s what worked for me:
Update Laravel config
In config/sanctum.php
, set your front-end domain:
' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost:3000')),
And in .env
:
SESSION_DRIVER=cookie
SANCTUM_STATEFUL_DOMAINS=localhost:3000
Enable cookies and credentials
In React:
axios.defaults.withCredentials = true;
await axios.get(`${BaseURL}/sanctum/csrf-cookie`);
This call ensures the session cookie and CSRF token are in sync.
Manually Include CSRF Token
If you’re not using Sanctum, I made a custom route in Laravel:
// routes/web.php
Route::get('/csrf-token', function () {
return response()->json(['csrf' => csrf_token()]);
});
Then in React:
csrf = await axios.get(`${BaseURL}/csrf-token`);
const token = csrf.data.csrf;
And I passed it in headers:
: { "X-CSRF-TOKEN": token }
Also make sure to enable CORS correctly on Laravel using the barryvdh/laravel-cors
package or Laravel 8’s native HandleCors
middleware.
A Reusable Delete Function
Once I understood the issue, I created a function that handles the CSRF token properly and deletes the charity:
function deleteCharity(id) {
try {
// Step 1: Get the token
const { data } = await axios.get(`${BaseURL}/csrf-token`);
// Step 2: Set up form data
const formData = new FormData();
formData.append("charity_id", Number(id));
formData.append("_method", "patch");
// Step 3: Send POST request
const response = await axios.post(`${BaseURL}/userManagement/deleteCharity`, formData, {
headers: {
"Content-Type": "multipart/form-data",
"X-CSRF-TOKEN": data.csrf,
},
withCredentials: true,
});
console.log("Deleted successfully:", response.data);
resetAll();
} catch (err) {
if (err.response && err.response.status === 419) {
console.error("CSRF token mismatch - try reloading the token.");
} else {
console.error("Error deleting charity:", err.message);
}
}
}
Extra Tip for Developer
I built a small wrapper for Axios that always fetches a CSRF token before sending state-changing requests. It looks like this:
function axiosWithCSRF(config) {
const { data } = await axios.get(`${BaseURL}/csrf-token`);
config.headers = {
...config.headers,
"X-CSRF-TOKEN": data.csrf,
};
config.withCredentials = true;
return axios(config);
}
Now I can reuse it like:
await axiosWithCSRF({
method: 'post',
url: `${BaseURL}/userManagement/deleteCharity`,
data: formData,
});
Final Thought
When building a front-end in React outside Laravel, it’s easy to forget that Laravel’s CSRF protection is tightly linked to session state and token matching. I learned that just sending the token isn’t enough you need to ensure the session and cookies are properly synced.