How to Measure Real-World Site Speed With Field Data

How to Measure Real-World Site Speed With Field Data

Real user monitoring site speed means collecting timing measurements from the browsers of actual visitors, not from a synthetic test rig in a data centre. You get this data by shipping a small script to the page, letting it read the browser's own performance timeline, and posting the results back to an endpoint you control.

The reason to bother is that a lab score tells you how a page behaves on one machine, on one connection, at one moment. Field data tells you how it behaves on the hardware and networks your visitors actually have. The two often disagree, and when they do, the field data is the one that reflects revenue, bounce rate and support tickets.

What the browser already measures for you

Modern browsers expose a PerformanceObserver interface that emits entries as the page loads. You do not need to time anything by hand for the core metrics. The entries you care about most are navigation, paint, largest-contentful-paint, layout-shift, event and resource. Each has a startTime relative to navigation start, and most carry enough context to be useful without extra work.

The navigation timing entry is the backbone. It gives you domContentLoadedEventEnd, loadEventEnd, responseStart and transferSize. From those you can derive time to first byte, document download time and total load time without guessing.

const obs = new PerformanceObserver((list) => {
  for (const e of list.getEntries()) {
    send({ name: e.name, start: e.startTime, dur: e.duration });
  }
});
obs.observe({ type: 'largest-contentful-paint', buffered: true });

Note the buffered: true option. Without it you miss entries that fired before your observer was registered, which is common when the script loads late or is deferred. If your tag manager injects the snippet after the first paint, buffering is the difference between having data and having holes.

Real user monitoring site speed needs a transport that survives page unload

The hard part is not measuring, it is getting the numbers off the device. A normal fetch or XMLHttpRequest fired during unload will often be cancelled by the browser before it leaves. The reliable mechanism is navigator.sendBeacon, which hands the payload to the browser and lets it transmit after the page is gone.

navigator.sendBeacon('/rum', new Blob(
  [JSON.stringify(payload)],
  { type: 'application/json' }
));

The endpoint has to accept a POST with a JSON body, respond quickly, and not require credentials that could make the browser drop the request. If you are behind a proxy or CDN, check that it does not strip the Content-Type header or buffer the response, because a slow endpoint turns into lost samples.

For metrics that only settle at the end of the visit, such as cumulative layout shift or the final largest contentful paint, you should send on visibilitychange when document.visibilityState becomes hidden, not on unload. That event fires reliably on mobile, where unload frequently does not.

Deciding which numbers to act on

Raw timings are noise until you group them. The useful view is a distribution, not an average, because averages hide the slow tail that real users feel. Look at the median to understand the typical visit, and the high percentiles to understand who is suffering. A page with a good median and a terrible tail is a page with a subset of users on slow connections or old devices, and that subset is usually larger than you think.

Attribute every sample. At minimum record the connection type from navigator.connection.effectiveType, the device class from the user agent or client hints, the page path, and a coarse geographic signal from the edge. Without these you cannot tell whether a regression is a code change or a shift in who is visiting.

Watch for these signals specifically. A rising time to first byte points at your origin, your database or your cache hit rate, not at your front end. A stable first byte with a rising largest contentful paint points at render blocking resources or late loaded images. A rising cumulative layout shift points at content injected after paint, usually ads, banners or web fonts. A rising interaction latency points at long tasks on the main thread, often third party scripts.

Set thresholds in terms of the metric, not in terms of a pass or fail label. If your median largest contentful paint is fine but your high percentile is poor, the fix is usually to prioritise the hero image and defer everything below the fold, not to rewrite the whole page.

Sampling, privacy and volume control

You do not need every visit. Sampling at a fixed rate, for example one in ten, gives you a representative distribution at a fraction of the ingest cost, and it keeps you under any rate limits your endpoint has. Make the sampling decision once per page view and store it, so all beacons from that view agree.

Strip anything that could identify a person before the payload leaves the browser. Do not send full URLs with query strings, do not send raw user agent strings if a parsed device class will do, and do not send a persistent identifier. A random per page view token is enough to correlate beacons from the same visit.

On the server side, accept the beacon, validate the shape, and write to a queue. Do not do heavy processing in the request path. The browser will not wait for you, and a slow ingest endpoint silently reduces your sample rate in a way that biases the data toward fast connections.

Reconciling field data with your lab tests

Field data and lab data answer different questions, and you should keep both. The lab test is a controlled experiment: change one thing, rerun, see the effect. The field data is the outcome: what users actually experienced across the whole population. When a lab test shows an improvement and the field data does not, the change probably affected a code path that few visitors take, or the bottleneck was elsewhere.

The practical loop is to use field data to find where the problem is, then use a lab test to isolate and fix it, then confirm in the field that the fix moved the distribution. If you only ever look at one side, you will either chase regressions that no user feels, or ship fixes that never land.

Start by adding the beacon script to one template, on one high traffic page, and let it run for a full week before you draw conclusions. Verify the endpoint is receiving samples by tailing your access log, and check that the payload shape matches what your collector expects. Once the pipeline is trustworthy, extend it to the rest of the site and set up a weekly review of the median and the high percentiles, not a daily one, because day to day variation will otherwise send you chasing ghosts.

Related articles

Subscribe to our newsletter

Get the latest hosting tips, performance insights, and industry news.