Search code examples
google-chromewebkitweb-analyticssendbeacon

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


I have the following code to send asynchronous HTTP request using sendBeacon method,

var data = {
 name: 'test',
 uniqueId: Math.random()
};
var blob = new Blob([JSON.stringify(data)], {type : 'application/json'});
navigator.sendBeacon('http://example.in/data/post', blob);

This code has worked fine for a long time. Currently, due to security issues in chrome https://bugs.chromium.org/p/chromium/issues/detail?id=490015, we see the error "Failed to execute 'sendBeacon' on 'Navigator': sendBeacon() with a Blob whose type is not CORS-safelists MIME-type is disallowed experimentally. See http://crbug.com/490015 for details."

Is there any workaround to send JSON data by modifying request headers using the same sendBeacon API till the issue is fixed? It'll be useful for sites depending on this API to continue to use till a fix is made. Suggestions on using XHR to post data are not useful.


Solution

  • Short Answer: The issue has been resolved as of 2021.

    Long Answer(background): I opened this issue some time back when I worked on timeonsite JS development. It used to work well in Chrome, Firefox & many other browsers. But, after few months, suddenly I got above CORS safelist error and failed to save time on site data real-time. It forced us to fallback on Localstorage which will result in (N-1) pageviews & subsequently lossy time-on-site data, a crucial web analytics metric, during each user session. And we have been eagerly waiting for this issue to be resolved from browser vendors. This is the configuration we used for capturing real-time that depends directly on sendBeacon() API

    <script type="text/javascript">
        var Tos;
        (function(d, s, id, file) {
            var js, fjs = d.getElementsByTagName(s)[0];
            if (d.getElementById(id)) return;
            js = d.createElement(s);
            js.id = id;
            js.onload = function() {
                // save with XMLHttpRequest (sync=true is blocking approach) or sendBeacon(preferred approach)
                var config = {
                    trackBy: 'seconds',
                    callback: function(data) {
                        console.log(data);
    
                        // give your endpoint URL/ server-side URL that is going to handle your TOS data which is of POST method. Eg. PHP, nodejs or python URL which saves this data to your DB
                        var endPointUrl = 'http://localhost:4500/tos'; // replace with your endpoint URL
    
                        if (data && data.trackingType) {
                            if (data.trackingType == 'tos') {
                                if (Tos.verifyData(data) != 'valid') {
                                    console.log('Data abolished!');
                                    return; 
                                }
                            }
                            
                            // make use of sendBeacon if this API is supported by your browser.
                            if (navigator && typeof navigator.sendBeacon === 'function') {
                                data.trasferredWith = 'sendBeacon';
                                var blob = new Blob([JSON.stringify(data)], {type : 'application/json'});
                                navigator.sendBeacon(endPointUrl, blob);
                            }
    
                            /*else {
    
                                // XMLHttpRequest begins..
                                // XMLHttpRequest with sync=true (blocking approach)
                                // XMLHttpRequest code block here to post your data
                            }*/
                        }
                    }
                };
    
                if (TimeOnSiteTracker) {
                    Tos = new TimeOnSiteTracker(config);
                }
            };
            js.src = file;fjs.parentNode.insertBefore(js, fjs);
        } (document, 'script', 'TimeOnSiteTracker', 'https://cdnjs.cloudflare.com/ajax/libs/timeonsite/1.1.0/timeonsitetracker.min.js'));
    </script> 
    

    As you saw above, we have been commenting out sendBeacon() code block for last few years due to the CORS safelist issue and depended on XMLHTTPRequest with async=false to post data which is blocking approach and not quite reliable on many browsers especially on mobile devices.

    Recently, it seems the issue has been resolved by browser vendors and sendBeacon() API is back for consumption. I tested across a number of browsers and it seems working fine. So, this issue is marked as "resolved" as of 2021. I would like you to add the devices/browsers that work well with version/year favorably mentioned for beforeunload/unload window events.