Say Bye with JavaScript Beacon

(hemath.dev)

75 points | by moebrowne 5 days ago ago

64 comments

  • vince14 2 days ago ago

    Their fetch call is missing `keepalive: true`.

        When set to true, the browser will not abort the associated request if the page that initiated it is unloaded before the request is complete. This enables a fetch() request to send analytics at the end of a session even if the user navigates away from or closes the page.
    
    https://developer.mozilla.org/en-US/docs/Web/API/RequestInit...
  • AndrewStephens 2 days ago ago

    The beacon API has exactly one use - reliable analytics. Sending a ping on leaving the page is a pretty contrived example - the beacon API is useful at any time.

    I use the beacon API in my homebuilt hit-counter. It means that even if the user navigates away before the hit is registered, the request will still have a fair chance of being received by my server.

    Of course there are other ways of doing this, but who wants to muck around with service workers or websockets when there is a simple, well-supported API that does exactly what you want?

    • Thorrez 2 days ago ago

      Could you count hits on the server when serving the page?

      I guess caching might break that depending on cache settings. And for SPAs, it might not work as desired.

      • dspillett 2 days ago ago

        Calling out as the page closes, assuming you called out at out listed too, tells you how long the user was there. Not entirely reliable of course as they could have opened it in the background so had it open a while before dismissing it without looking at it at all.

        • Thorrez a day ago ago

          I should have been clearer. I was replying to the "I use the beacon API in my homebuilt hit-counter." part.

      • AndrewStephens 2 days ago ago

        There are several reasons that you cannot just count the number of times your server has served the page.

        The biggest is bots - on my site (and I assume most others), legitimate requests are far, far outweighed by bots. Sometimes you can tell that they are bots but it is very common for bots to pretend to be real users. Oftentimes you can't tell from a single request but only but looking at the pattern of requests.

        Some bots do take the trouble of running scripts on the page but thankfully they are in the minority so far.

        Caching would certainly break the system as well, in the other direction.

        • immibis 2 days ago ago

          Why do we automatically suppose that bot requests are "not legitimate" btw?

          • wredcoll 2 days ago ago

            For the same reason we assume a random tiger wants to eat us.

    • LunaSea 2 days ago ago

      Is the Beacon API functional nowadays? I remember that a two or three years ago benchmarks showed that the original window.onunload (I believe) was able to log more event than the new Beacon API.

    • baxuz 2 days ago ago

      One downside is that it's not available in web workers.

    • jauntywundrkind 2 days ago ago

      One concern I have is that interactions on sites like HN might not have gotten to the server. Wouldn't this be useful to make sure something like an upvote gets through, even if I navigate away quickly?

    • stevage 2 days ago ago

      >Sending a ping on leaving the page is a pretty contrived example

      I don't think it's a contrived example. It seems to be one of the main use cases. From the spec:

      > The main use case for the Beacon API is to send analytics such as client-side events or session data to the server. Historically, websites have used XMLHttpRequest for this, but browsers do not guarantee to send these asynchronous requests in some circumstances (for example, if the page is about to be unloaded). To combat this, websites have resorted to various techniques, such as making the request synchronous, that have a bad effect on responsiveness. Because beacon requests are both asynchronous and guaranteed to be sent, they combine good performance characteristics and reliability.

  • tux3 2 days ago ago

    If you'd like, you can also Say Bye to Say Bye with JavaScript Beacons in about:config by setting beacon.enabled to false

    • bilekas 2 days ago ago

      This would be nice but I can't see any option for Chromium/Brave inside the chrome://flags .. Do you know if there is one ?

    • ravenstine 2 days ago ago

      This mostly works, but I have come across a minority of sites that break because of this. Whether that's a good thing or not is up to interpretation (I keep it disabled).

  • junon 2 days ago ago

    > Maybe it’s for for analytics or even auto-logout when they leave the website.

    Yeah, don't do either of these things. Don't log me out when I close the tab. Don't spy on me after I close the tab.

    Very simple!

    • hdjrudni 12 hours ago ago

      > Don't log me out when I close the tab.

      What about being logged out of a real-time chat with your friend? Wouldn't you like them to know you are no longer connected? Or do you prefer to trick them with a little green 'connected' bubble that can no longer update?

    • afavour 2 days ago ago

      > Don't spy on me after I close the tab.

      Maybe a small nit but it isn't spying on you after you close the tab. It is spying on you the moment you close the tab and no later.

      • junon 2 days ago ago

        If it's performing a request upon closing the tab, only two options are available:

        1. it delays closing until it completes, which is a performance hit, or

        2. it's performing more website specific background work when I've told the browser not to by hitting the Close Tab button.

        • subscribed 2 days ago ago

          Interrupt the request, clean up after the tab.

          That's all. The second option is garbage collector. Not sure why the website is entitled to execute any code when I decided to close it.

      • 01HNNWZ0MV43FF 2 days ago ago

        Don't spy on me

  • bilekas 2 days ago ago

    So this is kind of interesting, and I had a look at the spec.

    > The data parameter is the BodyInit data that is to be transmitted.

    However I couldn't find any information in the Spec itself about the size of the data until it said this :

    > The user agent imposes limits on the amount of data that can be sent via this API: [...] If the amount of data to be queued exceeds the user agent limit [...] , this method returns false; a return value of true implies the browser has queued the data for transfer. However, since the actual data transfer happens asynchronously, this method does not provide any information whether the data transfer has succeeded or not.

    Am I missing something or does this seem as unreliable as any other method for achieving this ?

    How about, and this might be a crazy idea, letting the users leave without annoying them ?

    • lifthrasiir 2 days ago ago

      Technically speaking a conforming agent can ignore every single sendBeacon call by always returning false. And that's a point: beacons are opportunistic asynchronous requests. You can't do anything when they error anyway, so they should be regarded as hints that agents may or may not honor.

      • bilekas 2 days ago ago

        Okay so it's basically just a baked in implementation of what people did already but instead of using onUnload they have more visibility into the client context. Seems dirty in a way, the agent could basically internally call the same thing and send any data to their own server on every page?

        For example, firefox, If they wanted, could record all your browsing history without notifying you? For any bad actor this seems like an amazing attack point.

        • swiftcoder 2 days ago ago

          Firefox could do that anyway, whether or not this API exists. Browser implementors need to be at least moderately trustworthy, or no one will use them.

  • lapcat 2 days ago ago

    [self-promotion:] My Safari extension StopTheMadness Pro disables the Beacon API.

    • srameshc 2 days ago ago

      Interesting. Could you elaborate on the rationale behind disabling the Beacon API?

      • snickerdoodle12 2 days ago ago

        It's solely created for advertisers to be able to implement their tracking more reliably. There is almost no way to use it in a way that benefits the user.

        • Chabsff 2 days ago ago

          Yes, but you could also say that it was created so that advertisers stop doing complete nonsense like tracking pixels and other roundabout ways of accomplishing the same thing in ways that are sometimes (often?) detrimental to the user.

          Sure, it's a form of capitulation to advertisers, that doesn't necessarily mean that it doesn't benefit the user.

          If advertisers are going to be doing that stuff no matter what, corralling them has advantages. If anything, it makes creating an extension like OP's a lot easier than playing whack-a-mole with a litany of random techniques.

          • snickerdoodle12 2 days ago ago

            Sure, which is exactly the rationale for disabling it.

      • vinnymac 2 days ago ago

        Not OP. It’s mostly used for tracking user behavior, and is rarely critical functionality for a given web page.

    • zenethian 2 days ago ago

      You're a hero, and I love your extension. Thank you for all of your hard work.

  • meindnoch 2 days ago ago

    >Sometimes we want to send a piece of data to our servers when user leaves our website or webapp.

    I've never.

    >Maybe it’s for for analytics or even auto-logout when they leave the website.

    Just don't persist the auth token? Analytics is blocked at the DNS level anyway.

    • can16358p 2 days ago ago

      There might be some sensitive applications where server might want to immediately revoke credentials server-side though.

      • meindnoch 2 days ago ago

        And what will this hyper-sensitive application do if I yank the power cable from the computer? Or if I quit the browser with kill -9?

        See, this is one of those "features" that clueless PMs ask their developers to implement, not having the technical knowledge to realize that their idea is unsalvageable. My other favorite is email address "validation" with ad hoc string format checks.

        • can16358p a day ago ago

          It's just an extra measure, not protecting the server from a malicious user, but an honest user's potential mismanagement of credentials.

  • Zealotux 2 days ago ago

    You may not want to rely too much on `beforeunload` events though as it is unreliable on mobile, a good practice if you want to send analytics if to listen to the page visibility:

      document.addEventListener('visibilitychange', () => {
        if (document.visibilityState === 'hidden') {
          navigator.sendBeacon(...);
        }
      });
    
    Of course it can't strictly be considered a "page leave" but for mobile users it's often your best bet: https://developer.chrome.com/docs/web-platform/page-lifecycl...
  • paultopia 2 days ago ago

    How about don't instead

  • azangru 2 days ago ago

    I remember when beacons were recently discussed here, someone mentioned the fetchLater API: https://developer.mozilla.org/en-US/docs/Web/API/fetchLater_...

  • daveoc64 2 days ago ago

    I was thinking this might be useful for locking records while users are looking at them, but that's probably better served with a periodic ping that ends when the user leaves the page.

  • stevage 2 days ago ago

    I tried to figure out how much data you can send exactly. It's not very clear from the spec:

    > If the amount of data that can be queued to be sent by keepalive enabled requests is exceeded by the size of transmittedData (as defined in HTTP-network-or-cache fetch), set the return value to false and terminate these steps.

    Does anyone know?

  • Arch-TK 2 days ago ago

    Surely if you want to reliably do some action when a user leaves your page then some persistent connection which dies is by far the most reliable option...?

    I'm no frontend expert but cant you just open a websocket and when the websocket dies from the server's perspective, you have your cue to do whatever cleanup you wanted to do?

    I also agree with the others here that there shouldn't really be a _need_ for this kind of feature.

    • Etheryte 2 days ago ago

      Keeping a websocket alive would work, but then you're stuck holding a websocket for all of your users for the full session. Fine if you need a socket anyway, pretty wasteful if you don't. Sockets are pretty cheap these days, but I wouldn't really give this serious thought for a page with a lot of users.

      • Arch-TK a day ago ago

        Holding a socket is very cheap. Unless websockets introduce some overhead I am not aware of.

    • bilekas 2 days ago ago

      > I'm no frontend expert but cant you just open a websocket and when the websocket dies from the server's perspective, you have your cue to do whatever cleanup you wanted to do?

      If I'm not mistaken, and I very well might be, it's kind of the same, however a WS connection here may be less reliable, infact if the user has a spotty connection, when you think "That user left", in the case of a local cleanup, okay that could run on the client side, however this beacon seems to be aimed at sending data back to the server when you leave, in the case of a dropped connection, you wont be able to send the data back anyway to the server.

      For this 'browser' based implementation under the hood, the browser *should* have more insight into the real intention of the users exit.

      • Arch-TK a day ago ago

        I really wouldn't want to be doing anything that requires reliability on the back of either solution as user agents can just opt to never send beacons.

    • afavour 2 days ago ago

      You're right that a persistent connection is the most reliable option. But it's also significantly more difficult to scale and will cause battery drain on mobile devices. The Beacon API is a better alternative for not-entirely-essential cases like analytics.

      • Arch-TK a day ago ago

        Scale wise, from the served side, it these are inactive TCP sessions at the end of the day. There isn't a serious scalability problem that doesn't exist with beacons, except that you might need to create some new software to support this.

        A persistent connection when a device is awake, as long as you're not sending data over it, will be nothing compared to the screen power draw.

        On the other hand, most phones will aggressively unload your app when the screen goes off or when its not in focus thereby killing your socket.

        I don't think there are actually many downsides to this as long as (afaict this is the case) websockets are just HTTP layer sockets downgraded to plain TCP.

  • chaz6 2 days ago ago

    In networking, there is a similar concept called "dyinggasp" in which a device will send a signal when power is lost. This can help the operator determine if a service is likely to be down due to a power failure or a signal interruption.

  • afavour 2 days ago ago

    Interesting to see so much resistance to the Beacon API. I think it's great. I've gotten a lot of useful data over the years via user analytics and routing all analytics requests through a dedicated API makes it trivial for users who care to disable it.

    • wrsh07 2 days ago ago

      Something many people refuse to believe is that "understanding your users" doesn't have to be nefarious or about serving ads.

      It's important to realize that key assumptions your app makes (nobody is accessing my b2b saas app from a phone) are incorrect. It's important to understand user workflows.

      There are higher touch ways to do this (user research is great!), and you should let users opt out, but someone trying to improve the user experience also needs to understand how users are defying their expectations

  • Hnrobert42 2 days ago ago

    Could you send the beacon every five minutes? When the beacon stops coming in for 30 minutes assume that the tab closed or the user went off-line.

    • ivanjermakov 2 days ago ago

      > every five minutes

      This is tricky in JavaScript, because timers are asleep/throttled in inactive tabs: https://developer.mozilla.org/en-US/docs/Web/API/Window/setT...

      And if onbeforeleave analytics is not evil, doing stuff in inactive tabs is certainly is.

    • hoppp 2 days ago ago

      You could just have a websocket open then. But its unreliable because if the user's internet cuts out maybe they still on the tab, so watching websocket disconnect is not perfect either

  • dmvjs 2 days ago ago

    this isn't new at all, ten years ago I used it to open an Android app without a user gesture https://paul.kinlan.me/deep-app-linking-on-android-and-chrom...

  • DanielHB 2 days ago ago

    This is neat, but don't service-workers persist for like 5s (non-deterministic) before being unloaded?

  • Brysonbw 2 days ago ago

    One of my favorite Web APIs

  • tuzemec 2 days ago ago

    Major hm... analytic tools... like MixPanel support this.

  • andrewstuart 2 days ago ago

    Where has this been hiding how did I not know about this?

  • hoppp 2 days ago ago

    I didn't know it existed, it is handy

  • righthand 2 days ago ago

    Can I disable this in my Chrome browser? I don’t want tracking companies to have more convenience.

    In Firefox you can disable it by beacon.enable = false.

  • mediumsmart a day ago ago

    Bye JavaScript.

  • RShackleford 2 days ago ago

    [dead]