Stop Coralogix RUM From Stealing Your Early Network

Coralogix RUM beacons during load and steals network from your own LCP. Buffer the events and flush them on page hide instead.

Arjen Karel Core Web Vitals Consultant
Arjen Karel - linkedin
Last update: 2026-06-06

Stop Coralogix RUM from stealing your early network

A RUM tool has one job: measure the experience your users actually get. Coralogix does the opposite during load. It starts beaconing the moment you call init, so if that call sits in your main bundle the SDK fires its first batch into the load and hydration window and steals network from the very LCP it is meant to be measuring. 

The tool first hurts the number, then reports it. That is backwards. So I write a workaround that buffers the data and sends it to the Coralogix API on page leave (just like Core/Dash does by default so if you are looking for a better RUM tool consider switching).

Coralogix thinks it is more important than your page

Look at what that beacon is competing with. Your hero image. Your fonts. The script that hydrates the page. On fiber you might never notice it. But your users are not all on fiber. On a phone, on a train, on hotel wifi, bandwidth is scarce, and that beacon fights your own content for it. The visitors on the worst connections they will notice it. Ant for what??

And here is the part that should bother you: there is no reason for it all that early data. None. The data is just about a visit that is still happening. Nobody is reading it live (and if they are that would be really creepy). It can sit in memory and go when the visit ends, cost nothing, and lose nothing. Sending it during load is all downside. It can only slow the page, and it buys you nothing. That is stupid. So take back the power: capture every event, send none of them until the visit is over.

Alright, enough ranting. Time to fix. This is how you make coralogix behave correctly!

Buffer everything, send nothing

Coralogix gives you a beforeSend hook. Return the event and it sends. Return null and it drops. So push the event into an array first, then return null. Every event captured, nothing on the wire.

import { CoralogixRum } from '@coralogix/browser';

const PUBLIC_KEY = '<YOUR_PUBLIC_KEY>';
const APPLICATION = 'my-app';

// Capture every event, send none of them live.
const buffer = [];

CoralogixRum.init({
  public_key: PUBLIC_KEY,
  application: APPLICATION,
  version: '1.0.0',
  coralogixDomain: 'EU1',
  beforeSend: (event) => {
    buffer.push({ event, t: Date.now() }); // stamp now, flush later
    return null;                           // suppress the SDK's own beacon
  },
});

I checked that this catches what matters, because beforeSend reads like an error-only filter in the docs. It is not. It fires for every event type: the init snapshot, resource timing, every web vital, user interactions, and errors. The SDK keeps doing its real job, measuring the whole page. It just goes quiet on the network until you tell it to flush.

Flush it on the way out

Now send the buffer when the page is hidden. This is where the naive version dies. You reach for navigator.sendBeacon, because that is the textbook way to send on unload. It does not work here. The ingress authenticates with an Authorization: Bearer header, and sendBeacon cannot set request headers. It returns true and 403s in silence. Use fetch with keepalive instead. It survives unload the same way, and it lets you set the header.

One more thing the beacon hid from you: the ingress does not take raw events. The SDK wraps each one in a span and posts { logs: [...] }. So rebuild that shape before you send.

const INGRESS = 'https://ingress.eu1.rum-ingress-coralogix.com/browser/v1beta/logs';
// swap eu1 for your region (us1, us2, eu2, ap1...) to match coralogixDomain

// The ingress wants the SDK's span shape, not raw events. Rebuild it.
function toCxSpan({ event, t }) {
  const hasStack = !!(event.error_context
    && event.error_context.original_stacktrace
    && event.error_context.original_stacktrace.length);

  return {
    version_metadata: event.version_metadata,
    applicationName: APPLICATION,
    subsystemName: 'cx_rum',
    severity: (event.event_context && event.event_context.severity) || 3,
    isErrorWithStacktrace: hasStack,
    timestamp: t,
    text: { cx_rum: Object.assign({}, event, { timestamp: t }) },
  };
}

function flush() {
  if (!buffer.length) return;
  const logs = buffer.splice(0).map(toCxSpan); // drain: a second call sends nothing

  fetch(INGRESS, {
    method: 'POST',
    keepalive: true, // survives unload, and unlike sendBeacon it can set headers
    headers: {
      'Authorization': 'Bearer ' + PUBLIC_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ logs, skip_enrichment_with_ip: false }),
  }).catch(function () {});
}

document.addEventListener('visibilitychange', function () {
  if (document.visibilityState === 'hidden') flush();
});
window.addEventListener('pagehide', flush);

splice drains the buffer, so a second hidden event sends nothing. visibilitychange to hidden is the reliable signal, even on mobile where unload never fires. pagehide is the backstop. One caveat: keepalive shares a 64KB budget across in-flight requests, so a long session that buffers thousands of resource entries can overflow it. If you collect heavily, chunk the logs array into a few smaller posts.

What it costs

You are now rebuilding Coralogix's format by hand, be carefull! The session_context is a bit thinner than the SDK's own. Coralogix fills in the session id, user agent, and device after beforeSend runs, so your hand-built spans carry less of that detail than a normal beacon would. For Core Web Vitals capture it does not matter. For deep session analytics it might. And confirm a 200 in your Network tab on first deploy, because a wrong key or a tightened CORS rule fails the same silent way the beacon did.

If maintaining a wire format you do not own sounds like a bad time, the blunt alternative is to defer CoralogixRum.init() until after hydration and let the SDK send normally from there. You lose the pre-init loading metrics, but you keep your sanity. Pick your trade.

About the author

Arjen Karel is a web performance consultant and the creator of CoreDash, a Real User Monitoring platform that tracks Core Web Vitals data across hundreds of sites. He also built the Core Web Vitals Visualizer Chrome extension. He has helped clients achieve passing Core Web Vitals scores on over 925,000 mobile URLs.

I write the code, not the report.

I join your team for 1 to 2 sprints. I set up the monitoring and make sure your team keeps the metrics green after I leave.

Get in touch
Stop Coralogix RUM From Stealing Your Early NetworkCore Web Vitals Stop Coralogix RUM From Stealing Your Early Network