Beacon API is broken
sendBeacon is a browser method to transfer data to the backend prior to unloading of a web page. In 2021, after studying over 5 million pageviews we strongly recommend not to use Beacon API in production.
Note: we're updating this document after summer 2022 to get an updated status
5,130,741 page views sampled • 3,217,740 successful unloads • 628 ignored browsers with less than 100 samples.
Regardless of browser version or operating system.
Regardless of the browser brand.
Browsers with the biggest amount of failures
Browsers with least amount of failures
How we got here
Volument is a new kind of analytics service focusing on privacy, insights, and content. One major difference between Volument and others is how the page view data is collected. While others send data when the page is loaded, Volument attempts to send the data after knowing how the page and it's viewports were consumed.
Obviously, we opted for Navigator.sendBeacon() to send this page consumption info to our servers. It is absolutely a perfect fit for our use case because page engagement data is only available after the visitor has consumed the content.
It solves all of the problems with submission of analytics data: the data is sent reliably, it's sent asynchronously, and it doesn't impact the loading of the next page.
Like most frontend developers, we use the latest browsers while developing a service, so we didn't notice anything unusual while developing the product because, as we later found out, only 3% of the requests fail on the latest Chrome on Mac.
We were quite confident that our data tracking was properly setup. According to caniuse.com the beacon API is supported by 93.5% of the browsers (May 2022), and we used fallback data collection mechanism in place for browsers that don't support the API.
However, after using sendBeacon for a few days our beta customers’ websites, we noticed big data inconsistency between Google Analytics and Volument. Volument pageview counters were 30-40% smaller on average.
To find out the problem, one of the first things we did was to start tracking the performance of sendBeacon.
We currently have 38 beta customers sending data to our servers. Together they produce enough sample data to measure the performance of any browser-based technology in a fair amount of time. With the sendBeacon we did the following:
- Increment a counter when a page is loaded
- Increment another counter when a page is unloaded
- Measure failure from the difference of these two counters
For the page loads we used the standard POST method in the
XMLHttpRequest object and for the unloads we did the following:
We tried both
unload events with similar results: some of the calls succeed and some fail. The problem would be easy to debug if the call would always fail, but that is not the case, unfortunately. The problem is on the built-in browser method, which cannot be trusted. No browser vendor can guarantee reliable data delivery.
Our sample data is collected upon the
beforeunload event because it is fired before the unload event and is thus more likely to succeed. We used bowser for browser detection.
The `visibilitychange event
Due to our measurements, the official MDN documentation now recommends using
visibilitychange event together with sendBeacon. We're going to make another test with this event after the summer and reveal our results on this blog. We expect it to give similar results, but we'll see.
- On this sample set, about 30% of browsers that claim to support the beacon API failed to deliver the data to our servers when the page was closed, which is the whole purpose of the sendBeacon call.
- Linux fails 94.1% of the time
- It's extremely hard to feature-detect beacon support. The call might work on the page, but does it work when the page is closed? Seems you must look for supporting browsers from the above list and accept a 1-3% failure rate.
- The potential fallback mechanisms (XHR, Image) are likely even more unreliable when the page is closed since their delivery is not guaranteed.
- It's not you — it's the browser vendors to blame: their built-in browser implementations are broken and cannot be trusted.
Thanks to developer community feedback, we removed iOS from the list. This browser has known issues delivering on the promises of beacon API. The sendBeacon event itself is working, but the unload/beforeunload events are not fired. The
visibilitychange event is probably the right event on mobile devices.
In the end, we started sending while the visitor is interacting with the page.
Of course, we would absolutely love to use the beacon API to send all data just once with zero performance cost. Now we need to send multiple events, which is not as cool and burdens our servers.