DEV Community

Cover image for How to Securely Send a Request When Closing Tabs
Zachary Lee
Zachary Lee

Posted on • Originally published at webdeveloper.beehiiv.com on

How to Securely Send a Request When Closing Tabs

If the website wants to send analytics after the user has finished the page, but the page may have been unloaded by then, sending an asynchronous request may not be sent by the browser at this point. So what should we do?

First, we might think of the beforeunload event. It enables the web page to trigger a confirmation dialog to ask the user if they want to leave the page. When the user enters unsaved data but accidentally closes the page or refreshes the page, the data will be lost. The use of beforeunload allows the user to confirm twice. Like the screenshot below:

However, clicking Leave or Cancel does not have a corresponding callback function for us to use, so it can only be used as a secondary confirmation.

Next is the unload event. This event is fired when the document is unloaded. It’s after the beforeunload event. If we send an asynchronous request in this event, as I said at the beginning, the browser may choose not to send this asynchronous request, or it may abort this request.

So can we send blocking synchronous XMLHttpRequest requests like below?

The answer is yes. But this prevents the document from being unloaded, which slows down the browser to navigate to the next page. The next page can’t avoid this, because the new page looks slow, even if it’s the previous page’s fault.

Similar to this: create an <img> element and set its src, create a synchronous loop that does nothing for a few seconds, etc.

So these are not good solutions. Fortunately, browsers provide a targeted API for this — Navigator.sendBeacon()

This event allows profiling data to be sent asynchronously at the end of a page session without delay blocking the loading of the next page. And it guarantees that data is sent reliably.

Its syntax is this: navigator.sendBeacon(url [, data]). data is an optional parameter, it can be an ArrayBuffer, a TypedArray, a DataView, a Blob, a string literal or object, a FormData or a URLSearchParams object.

Here is a simple example:

You can see that I use the visibilitychange event here instead of the unload or beforeunload event. This is because both events are extremely unreliable. It is especially serious under mobile devices, such as when the user closes the browser application using the application manager of the mobile phone, when switching between different applications, etc.

Also, these two events are not compatible with modern browsers’ back/forward cache (bfcache). This is bad for performance. So the best event to signal the end of a user session is the visibilitychange event. In browsers that don't support it, the next best option is pagehide event, which is also not fired reliably, but which is bfcache-compatible.

Finally, let’s look at the type of data. When we use URLSearchParams like above, the request header content-type will be set to text/plain;charset=UTF-8. Similarly, if we use FormData, then the content-type of the request header will be set to multipart/form-data. But if we use Blob, we can customize request headers and request content like this:

Do you know other ways? Feel free to share.

If you find this helpful, please consider subscribing to my newsletter for more insights on web development. Thank you for reading!

Top comments (0)