Navigator.sendBeacon() to pass header information

I want to call an api when someone close the tab, so I tried to use navigator.sendBeacon() but the problem is we need to pass the Authorization token into it and sendBeacon does not provide that, so I found other solution that is more effective and very easy to implement.

The solution is a native fetch API with a keepalive flag in pagehide event.

Code

window.addEventListener('pagehide', () => {
  fetch(`<URL>`, {
    keepalive: true,
    method: '<METHOD>',
    headers: {
      'content-type': 'application/json',
      // any header you can pass here
    },
    body: JSON.stringify({ data: 'any data' }),
  });
});

FAQs / TL;DR Version

  1. Why should we need to use the keepalive flag?

    • The keepalive option can be used to allow the request to outlive the page. Fetch with the keepalive flag is a replacement for the Navigator.sendBeacon() API.
    • Learn more about it, please visit https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters
  2. What is PageLifecycle API

    enter image description here

    • Learn more about it, please visit https://developer.chrome.com/blog/page-lifecycle-api/
  3. From the Page Lifecycle image, shouldn't unload be considered as the best choice?

    • unload is the best event for this case but unload is not firing in some cases on mobile and it also does not support the bfcache functionality.
    • I also notice that when I am using unload then I am not getting proper output in the server log. why? IDK, if you know about it then comments are welcome.
    • Nowadays, It's also not recommended by the developers.
    • Learn more about why unload is not recommended: https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event#usage_notes
    • Learn more about pagehide: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event

First, note that navigator.sendBeacon is not supported in all browsers. See more detail about this function as well as currently supported browsers at the MDN documentation.

You do indeed create a blob to provide headers. Here is an example:

window.onunload = function () {
  const body = {
    id,
    email,
  };
  const headers = {
    type: 'application/json',
  };
  const blob = new Blob([JSON.stringify(body)], headers);
  navigator.sendBeacon('url', blob);
});

navigator.sendBeacon will send a POST request with the Content-Type request header set to whatever is in headers.type. This seems to be the only header you can set in a beacon though, per W3C:

The sendBeacon method does not provide ability to customize the request method, provide custom request headers, or change other processing properties of the request and response. Applications that require non-default settings for such requests should use the [FETCH] API with keepalive flag set to true.

I was able to observe some of how this worked through this Chromium bug report.


If you're using Chrome and you're trying to set the content-type header, you'll probably have some issues due to security restrictions:

Uncaught DOMException: Failed to execute 'sendBeacon' on 'Navigator': sendBeacon() with a Blob whose type is not any of the CORS-safelisted values for the Content-Type request header is disabled temporarily. See http://crbug.com/490015 for details.

See sendBeacon API not working temporarily due to security issue, any workaround?


As written in the Processing Model of sendBeacon :

Extract object's byte stream (transmittedData) and content type (contentType).

How extraction is performed is described here

What I've gathered is that the content type of the transmitted data is extracted, and it is set as the Content-Type of the HTTP request.

1) If a Blob object is sent, the Content-Type becomes the Blob's type.

2) If a FormData object is sent, the Content-Type becomes multipart/form-data

3) If a URLSearchParams object is sent, the Content-Type becomes application/x-www-form-urlencoded

4) If a normal string is sent, the Content-Type becomes text/plain

Javascript code to implement different objects can be found here

Tags:

Javascript

W3C