47

I am using navigator for communicating with the server , but problem is that we need to pass some header information as there is filter which recognise the request is from the valid source.

Can anybody help on this?

Thanks.

Vipul Panth
  • 5,221
  • 4
  • 16
  • 27
  • 2
    @scotty-h any update on this? are we allowed to set custom headers especially "Auth" or "Authorization" to allow requests to be white-listed for REST APIs? I think cors-safelist warning is removed by Chrome and Firefox as of mid 2021. We aim to use **sendBeacon()** with custom Auth headers a lot in [timeonsite tracker](https://saleemkce.github.io/timeonsite) for analytics, this is crucial metric. – webblover Dec 22 '21 at 16:23

8 Answers8

86

See the Navigator.sendBeacon MDN documentation for further information.

Create a blob to provide headers. Here is an example:

window.onunload = () => {
  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.

Scotty H
  • 6,432
  • 6
  • 41
  • 94
  • Yes you are completely correct as this is an experimental approach as per [docs](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) and only available in latest browser such as chrome v39. – Vipul Panth Jan 19 '17 at 07:19
  • 1
    Correct me if I'm wrong, but keepalive for fetch isn't even implemented in Chrome yet . https://groups.google.com/a/chromium.org/forum/#!topic/loading-dev/_F6oicQ3_F4 – Maciej Krawczyk Jul 09 '17 at 10:20
  • 4
    Unfortunately, this is not allowed anymore and will throw an error due to security reasons as of Chrome 59. See: https://crbug.com/720283 – Marcel Panse Jul 17 '19 at 10:41
  • 1
    @Scotty This doesn't works with Authorization headers – Ankit Jindal Jul 23 '21 at 06:24
  • As a follow-up to @MarcelPanse, it works again since Chrome 81. See: https://crbug.com/724929 – Afterlame Sep 07 '22 at 12:05
14

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?

  2. What is PageLifecycle API

    enter image description here

  3. From the Page Lifecycle image, shouldn't unload be considered as the best choice?

Nisharg Shah
  • 16,638
  • 10
  • 62
  • 73
7

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

Useful Angle
  • 928
  • 9
  • 16
  • 1
    3) Does not work in Chrome see:https://bugs.chromium.org/p/chromium/issues/detail?id=747787 the Content-Type is still `text/plain;charset=UTF-8` – Roland Starke Mar 26 '19 at 14:48
6

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?

BSMP
  • 4,596
  • 8
  • 33
  • 44
ersefuril
  • 809
  • 9
  • 16
1

After searching for an answer for this question I found out that for passing header with navigator we need to pass a blob object.

For example

var headers = {type: 'application/json'};
var blob = new Blob(request, headers);
navigator.sendBeacon('url/to/send', blob);
Vipul Panth
  • 5,221
  • 4
  • 16
  • 27
  • Did you mean to capitalize `new blob` so that it would be `new Blob`? – Scotty H Jan 18 '17 at 20:45
  • 1
    Also what is `request`? Can you cite any of your sources? – Scotty H Jan 18 '17 at 20:51
  • the request i am talking about is a list of ticket data that needs to be send while the browser closes along with a security header so that server can recognise the it as a authorise request. – Vipul Panth Jan 19 '17 at 07:15
1

Because the method sendBeacon(..) does not allow headers manipulation, I added them into the form as normal fields:

    const formData = new FormData();
    formData.append('authorization', myAuthService.getCachedToken());
    navigator.sendBeacon(myURL, formData);

Then on the host side I added a simple Middleware class (.Net) which catches POST requests without headers and copies them from the body:

    public class AuthMiddleware
    {
        ...
        ...
        public async Task Invoke(HttpContext context)
        {
            string authHeader = context.Request.Headers["Authorization"];
            if (authHeader == null && context.Request.Method=="POST")
            {
                context.Request.Headers["Authorization"] = string.Format("Bearer {0}",
                    context.Request.Form["authorization"].ToString());
            }

            await _next.Invoke(context);
        }
    }
1

Posting as an answer as I'm not allowed to post a comment under the answer:

For Chrome, issue with navigator.sendBeacon sending Blob for with non CORS-safelisted types was fixed in Chrome version 81 so this should be safe to use now. https://bugs.chromium.org/p/chromium/issues/detail?id=724929

For IE, an alternative in unload event is to use synchronous ajax request, as IE doesn't support sendBeacon but supports synchronous ajax call in my case.

Hai Ha
  • 11
  • 1
1

You can't send data with JSON after Chrome 39, has been disabled due to a security concern.

You can try to send data with plain text. But don't forget the parseing text from the backend.

Hasan Tezcan
  • 1,116
  • 1
  • 11
  • 23