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

Ligonsker's avatar

Can I send the CSRF token with navgiator.sendBeacon?

Hello,

Update:

So I understood that it's not possible to do that, I will then need to send the CSRF token in the data, and extract it in the backend. But it means I have to pass Laravel's CSRF check and do it myself.

What is a good way to do that then? i.e. to validate the token myself and prevent Laravel from checking it thus throwing me 419?

---- Original Post ----

I need to send the CSRF token in the header otherwise I get 419 error.

I found this post: https://stackoverflow.com/questions/40523469/navigator-sendbeacon-to-pass-header-information

But it's not helpful, the answers there either say to use fetch, or they say you can send headers, but then they only show:

const headers = {
    type: 'application/json',
};

That's only the type, what about the data itself which is the CSRF token:

let csrfToken = $('meta[name="csrf-token"]').attr('content')

Is it still not possible to send it via sendBeacon and I will need to use fetch as suggested with the keepalive flag, or, it can be done?

The following doesn't work either:

const url = "/some/url",
    data = new FormData(),
    token = $('meta[name="csrf-token"]').attr('content');

data.append("foo", "bar");
data.append("X-CSRF-TOKEN", token);
navigator.sendBeacon(url, data);
0 likes
4 replies
Ligonsker's avatar

I found a solution:

I added the sendBeacon endpoint to the $except array in the verifyCsrfToken:

class VerifyCsrfToken extends Middleware
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        '/beacon/endpoint'
    ];
}

and I will need to verify it myself.

But what is a good way to manually verify the tokens myself in the controller (Or not a controller, I can just make my own middleware, but how do I verify against the token like Laravel does?)

Snapey's avatar

You only need to verify it if the message contains data that modifies server side data. You dont need to send if if you are only indicating status

1 like
Ligonsker's avatar

@Snapey It's for sending some information that might be sensitive via web sockets (Some data to be called when dispatching the event later), that's why I wanted to be safer. Maybe it is really not necessary.

What I did now was to copy the entire Illuminate\Foundation\Http\Middleware\VerifyCsrfToken code to a new middleware, called VerifyCsrfTokenCustom.

There I extract the JSON of the beacon from the $request and I modified the getTokenFromRequest() method so that it will be able to extract it correctly.

Just in case I do decide to eventually use the CSRF protection(And not completely skip it as you suggested), is what I did a good way, or there's a simpler way to do it?

Ligonsker's avatar

@Snapey Omg, I just realized: getTokenFromRequest() looks for either the header called X-CSRF-TOKEN, OR, an input named _token:

protected function getTokenFromRequest($request)
{
    $token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');

    // ...
}

So instead of doing all the work I did there, I can simply do:

const url = "/some/url",
    data = new FormData(),
    token = $('meta[name="csrf-token"]').attr('content');

data.append("_token", token); // instead of data.append("X-CSRF-TOKEN", token);
navigator.sendBeacon(url, data);

No need for custom middleware or $except, simply changed the key from "X-CSRF-TOKEN" to "_token" 😅

It works!

Please or to participate in this conversation.