Fluxtail
Log Management Guides

RUM Real User Monitoring: A Practical Guide

Learn how real user monitoring works, what RUM can measure, and how to design field collection, sampling, privacy, alerts, and backend correlation.

By Fluxtail Engineering Updated

RUM, or real user monitoring, measures how real browsers experience a site or web application in production. A browser library records selected performance and error signals from actual page views, sends them to a collection service, and aggregates them by page, browser, device, release, or another approved dimension.

RUM is field measurement, not a controlled test. It reveals the range of conditions users encounter—different devices, network paths, cached resources, extensions, and interaction patterns—but it does not automatically explain the backend cause of a slow or failed experience. A sound implementation combines accurate browser measurement with privacy controls, representative sampling, explicit delivery behavior, and a safe way to pivot from a browser symptom to server-side evidence.

What Real User Monitoring Measures

RUM observes the client side of real sessions. Depending on the browser APIs and chosen SDK, it can collect:

  • navigation and paint timing;
  • Core Web Vitals and their diagnostic attributes;
  • resource timing that the browser is permitted to expose;
  • JavaScript errors and rejected promises;
  • route or view transitions when the application instruments them;
  • approved browser, device, release, and coarse network context; and
  • a pseudonymous correlation value that can be joined to backend evidence.

Elastic’s definition of RUM captures the core distinction: browser instrumentation monitors the real user experience inside the client application, unlike backend agents that observe server requests and responses.

RUM cannot observe every user or every cause. Browser support differs, privacy settings and blockers may stop collection, a user may leave before a batch is sent, and cross-origin security rules hide some resource detail. A page with no qualifying interaction has no INP value. A sampled dataset describes the sampled population, not every visit.

RUM, synthetic monitoring, APM, and logs answer different questions

RUM asks, “What did actual browser visits experience?” Synthetic monitoring asks, “Can this scripted journey complete from a controlled location under defined conditions?” Synthetic checks can run before users arrive and provide repeatable comparisons; they cannot reproduce the full production population.

Backend APM and distributed tracing explain server transactions and dependency paths when those systems are instrumented. Logs record discrete application, infrastructure, and security events. Neither automatically recreates the browser’s rendering or interaction experience. The browser can report a poor LCP even when the origin responds quickly, while backend logs can reveal the server error associated with a failed request that RUM only sees as a status or client exception.

Use the signals together without merging their meanings. The metrics versus logs guide explains why an aggregate trend and a discrete event serve different investigation steps.

Measure Core Web Vitals as Field Distributions

The current Core Web Vitals are Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS). Google’s documented “good” thresholds are:

  • LCP at or below 2.5 seconds;
  • INP at or below 200 milliseconds; and
  • CLS at or below 0.1.

These are assessed at the 75th percentile of page views. Google applies the same thresholds to mobile and desktop, although teams should still segment those populations to find device-specific problems. The Core Web Vitals threshold methodology explains both the values and the p75 cohort rule.

A percentile is not an average. If a route’s p75 LCP is 2.4 seconds, at least three quarters of included measurements are at or below 2.4 seconds; the remaining quarter can still contain serious slow experiences. Preserve sample count, time window, and cohort definition whenever reporting a percentile.

Do not combine incompatible populations. Compare the same route definition, device class, sampling policy, release, and time range. Keep mobile and desktop views available even though their “good” thresholds match. A global p75 can hide a regression confined to one browser, country group, or release.

LCP, INP, and CLS need diagnostic context

LCP represents perceived loading progress by measuring when the likely main content is rendered. Collect the metric and safe attributes such as a stable page template and release. Avoid sending the element’s text or raw DOM because it may contain personal or account-specific content.

INP measures responsiveness across click, tap, and keyboard interactions during the page lifecycle. It can be absent when a user never makes a measured interaction, or only scrolls or hovers. Missing INP must not be converted to zero or treated as a fast interaction. The INP documentation also explains an important coverage difference: CrUX can include interactions inside iframes, while top-level JavaScript APIs cannot observe cross-origin iframe contents. A JavaScript RUM dataset and CrUX can therefore disagree without either being corrupted.

CLS measures unexpected visual movement. Record enough stable context to identify the affected page template or component class, but do not capture page text or arbitrary selectors that include identifiers. For long-lived pages, verify how the SDK handles lifecycle changes, back/forward cache restoration, and repeated metric updates.

TTFB and FCP help diagnose, but are not Core Web Vitals

Time to First Byte (TTFB) and First Contentful Paint (FCP) remain useful diagnostic measurements. TTFB can help separate the initial response path from later rendering work. FCP marks when the browser first displays content. Neither is one of the three Core Web Vitals, and neither alone proves that the page became usable.

Field data and lab data also have different roles. Google’s Core Web Vitals tools guidance recommends field data for real-world measurement and lab tools such as Lighthouse for repeatable diagnosis. Field data is a distribution across real devices, networks, and behavior—not one representative run, as Google’s lab and field data comparison explains. Use a field regression to identify the affected cohort, then reproduce it in a controlled browser profile where possible.

Design a Browser Collection Pipeline

A practical RUM path has five boundaries:

  1. A browser SDK observes supported events and performance entries.
  2. Client-side code allowlists fields, removes unsafe values, applies sampling, and batches a bounded number of records.
  3. A collection endpoint validates origin, schema, size, rate, and field limits before accepting the batch.
  4. A processing layer normalizes versioned events without erasing the original measurement semantics.
  5. A RUM backend aggregates distributions and exposes dashboards, filters, alerts, and controlled diagnostic access.

Do not embed a secret in browser JavaScript. Any visitor can inspect the bundle and network requests. If an intake identifier must be public, treat it as routing information, not authentication. Protect the endpoint with narrow allowed origins where applicable, schema and size validation, rate controls, abuse monitoring, and server-side authorization for any privileged action. Origin checks are useful but are not a complete trust boundary by themselves.

Use versioned event names and stable types. A representative sanitized event might look like this:

{
  "schema_version": 1,
  "event_name": "web_vital",
  "observed_at": "2026-09-16T14:22:05.318Z",
  "metric": "LCP",
  "value_ms": 1834,
  "page_template": "/checkout/:step",
  "release": "web-2026.09.16.2",
  "device_class": "mobile",
  "browser_family": "Chromium",
  "sample_rate": 0.1,
  "correlation_id": "pseudonymous-short-lived-value"
}

The timestamp and values are illustrative. Keep units in field names or schema documentation, and prevent a field from changing type between releases. Use a route template rather than a full URL. Do not put an email, account number, search query, form value, or request body into the event.

Treat single-page navigation as an instrumentation decision

A traditional document navigation gives browser APIs a clear page lifecycle. Single-page applications often change routes without loading a new document. The definition and measurement of a “view,” soft navigation, route duration, or component transition therefore depend on the SDK and application instrumentation.

Document the exact event that starts and ends a view. Verify route changes, history navigation, interrupted transitions, and background tabs against the selected library. A transition to hidden can be temporary; it is a good time to flush, but it does not prove that the logical session ended. Detect a back/forward cache restoration with pageshow when event.persisted is true, count it consistently as a new page view, and reset page-visit metrics according to the metric implementation. The back/forward cache guidance explains this analytics boundary. Do not assume that installing a generic browser package automatically measures every framework transition. OpenTelemetry’s current browser JavaScript guide explicitly labels browser client instrumentation experimental and mostly unspecified, so validate its behavior before using it as a RUM contract.

Expect cross-origin resource timing gaps

The Resource Timing API exposes detailed timing for same-origin resources, but cross-origin attributes are restricted. A resource provider can return Timing-Allow-Origin to permit specified origins to see additional values. Without the required permission, values such as transfer size or detailed phases can be zero or unavailable. The W3C Resource Timing specification defines those restrictions.

Configure Timing-Allow-Origin only on resources whose timing should be exposed, and name approved origins instead of using a wildcard by habit. The header permits eligible Resource Timing attributes to be exposed; it does not bypass CORS, reveal server-internal work, or guarantee that every size attribute will be populated. RUM cannot force a third-party provider to reveal its internal phases. Treat missing cross-origin detail as unavailable, not as a zero-duration fetch.

Deliver Events Without Blocking Navigation

Browser delivery is best effort. Flush small bounded batches during the session and when the document becomes hidden; do not wait for unload as the primary mechanism. Mobile browsers may terminate a backgrounded page without running unload handlers.

For small analytics batches, navigator.sendBeacon() queues an asynchronous POST without delaying the next navigation. MDN recommends using it from visibilitychange when the document becomes hidden and warns that unload and beforeunload are unreliable, especially on mobile. The currently documented limit is 64 KiB for the user agent’s total queued beacon data, not a guaranteed per-event allowance, so keep every batch well below it. A minimal lifecycle pattern is:

document.addEventListener("visibilitychange", () => {
  if (document.visibilityState !== "hidden") return;

  const batch = takeSanitizedRumBatch({ maxEvents: 20 });
  if (batch.length === 0) return;

  const body = new Blob([JSON.stringify(batch)], {
    type: "application/json",
  });

  const queued = navigator.sendBeacon("/browser-telemetry", body);
  if (!queued) retainWithinBoundedMemory(batch);
});

The application-specific functions must enforce size, privacy, and memory limits. A true return means the browser queued the data, not that the backend stored it. Use a same-origin endpoint or verify the exact cross-origin and credential behavior. Do not retry without a cap; repeated retries can duplicate events and consume memory. Give every accepted event an ID only if the backend needs deduplication, and monitor accepted, rejected, malformed, and dropped batches at the collection boundary.

Avoid synchronous requests during shutdown. They delay navigation and still do not guarantee delivery. For important operational events, record the decisive server-side outcome in backend logs rather than relying on the browser to be the sole evidence source.

Sample Without Breaking the Denominator

High-traffic sites rarely need every performance event. Choose a representative sampling unit—often a page view or pseudonymous short-lived session—and make the decision consistently for all metrics needed in the same analysis. Record the sampling policy and rate with the event or dataset version.

Random representative sampling supports population percentiles when each eligible view has the intended inclusion probability. Deterministic sampling based on a privacy-safe rotating value can keep a short journey together, but it must not create a permanent cross-site or cross-period identifier.

Error oversampling is useful for diagnosis, but it changes the dataset. If all errors are kept while only a fraction of successful views are retained, raw error counts and rates are biased unless the query applies correct weights or uses a separate denominator. Keep the representative performance sample distinct from the diagnostic error stream, or store explicit sampling probabilities and use them correctly.

Never compare a sampled numerator with an unsampled denominator. For example, a JavaScript-error rate requires error events and eligible page views observed at the same collection boundary under compatible sampling. Track delivery loss as well: an intake outage can make a broken application appear quiet.

Make Privacy a Collection Rule, Not a Cleanup Job

Browser telemetry runs close to sensitive user input. Define an allowlist before enabling collection and reject unexpected fields at intake. By default, do not collect:

  • DOM or form text;
  • passwords, tokens, cookies, or Authorization values;
  • request or response bodies;
  • full URLs, query strings, or fragments;
  • email addresses, account names, or stable user identifiers;
  • precise IP-derived location;
  • arbitrary headers; or
  • session replay content.

Use stable route templates and coarse approved categories instead. Redact in the browser when possible, validate again at intake, and apply access, retention, deletion, and audit controls in storage. A field that is harmless on one route can contain personal data on another, so test actual rendered URLs and error messages rather than trusting field names.

Consent and legal requirements depend on jurisdiction, purpose, data, users, and organizational role. Record the purpose of each field, honor the applicable consent state before collection, and obtain qualified privacy or legal review. Do not present a banner or SDK setting as universal compliance.

Source maps improve JavaScript stack readability but can expose source structure. Keep original maps in a restricted release artifact store, upload them through the tool’s protected workflow, scope access, and delete them according to retention policy. Do not publish private source maps with public assets unless that disclosure is intentional.

Correlate Browser Symptoms With Backend Evidence Safely

A short-lived pseudonymous correlation ID can connect a browser-reported failure to backend logs. Generate a high-entropy value, attach it only to requests sent to approved application origins, validate its format and length at the server, and copy it into structured backend events. Do not treat the value as authentication or trust any identity claim attached by the browser.

W3C Trace Context provides standardized traceparent and tracestate headers for distributed tracing. It can be propagated when tracing is deliberately instrumented, but a trace ID is not a browser session ID. One session can contain many traces; a trace can cross multiple services. The Trace Context specification also includes privacy and security considerations, so do not put personal information into tracestate or trust incoming trace flags as authorization. OpenTelemetry’s context-propagation security guidance recommends treating incoming external context as potentially forged and avoiding propagation of internal context to untrusted external endpoints.

At the browser-to-server boundary, allowlist only the correlation headers the application needs. Do not dump every request header into logs. Backend events can safely include fields such as:

  • correlation or trace ID;
  • route template and HTTP method;
  • service, environment, and release;
  • bounded status or error class;
  • server-observed duration; and
  • a timestamp from the server’s clock.

These fields let an investigator move from “checkout INP regressed for release X” to requests and errors for the same release or correlation value. They do not create automatic causality. Compare timestamps and request boundaries, then verify the actual backend event.

Build Alerts Around Cohorts and Sample Size

Alert on a sustained change that requires a timely human decision, not every slow sample. A useful RUM alert defines:

  • the metric and exact cohort;
  • percentile or rate calculation;
  • minimum eligible sample count;
  • evaluation and comparison windows;
  • release, route, device, or browser segment;
  • missing-data behavior;
  • owner, evidence link, and first investigation step; and
  • recovery condition.

Use p75 to track Core Web Vitals classification, and consider p95 for severe tail experience when the cohort has enough samples. A p95 computed from a handful of visits is unstable. Low-traffic pages may need a longer window, an absolute count condition, and a synthetic journey rather than an aggressive percentile page.

For rates, keep numerator and denominator at the same observation boundary and sampling policy. Segment narrowly enough to locate a regression but not so narrowly that every browser version or URL becomes a noisy alert series. Prefer stable route templates and release identifiers over full URLs and unbounded labels.

RUM alerts complement, rather than replace, backend availability, error-rate, and SLO alerts. The alerting best-practices guide covers ownership, missing data, low traffic, grouping, and delivery-path tests in more detail.

Roll Out RUM in Controlled Stages

Start with one important user journey and a small representative sample.

  1. Define the question, eligible page views, route templates, owners, and success criteria.
  2. Inventory every collected field and document its purpose, type, retention, and access.
  3. Establish consent behavior and test that collection stays off when required.
  4. Instrument LCP, INP, CLS, page views, release, and sanitized error classes before adding broad context.
  5. Verify desktop and mobile, supported browsers, route transitions, iframes, back/forward cache, backgrounding, and blocked collection.
  6. Test normal navigation, collector rejection, network loss, duplicate delivery, oversized batches, schema changes, and intake outages.
  7. Compare RUM field results with repeatable lab measurements, expecting the populations to differ.
  8. Add representative sampling, then verify denominators before enabling error oversampling.
  9. Introduce correlation on approved origins and confirm that backend logs contain only allowlisted fields.
  10. Build dashboards and alerts only after sample counts, delivery loss, and missing-data behavior are visible.

Release the browser package like production code. Pin and review dependencies, limit its main-thread work, measure its own transfer and execution cost, and provide a kill switch. A monitoring library that creates long tasks or blocks application startup damages the experience it is measuring.

Evaluate RUM Tools Against the Measurement Contract

Choose a RUM tool by testing the workflow and boundaries that matter, not by counting dashboard widgets. A proof of value should answer:

  • Which browsers, frameworks, Web Vitals, errors, route transitions, and lifecycle cases are supported?
  • How does the SDK define page views and soft navigation?
  • How are sample rates applied, recorded, and changed?
  • Can error oversampling be separated from representative performance analysis?
  • Which fields are collected by default, and can an allowlist run before transmission?
  • How are consent, deletion, retention, residency, role-based access, and audit records handled?
  • Are source maps private, release-scoped, and access-controlled?
  • How does the intake resist abuse without putting a secret in JavaScript?
  • What happens during a network failure, tab backgrounding, duplicate send, or malformed batch?
  • Can dashboards show p75 and p95 with sample counts and stable cohort filters?
  • Do alerts support minimum traffic, missing data, releases, and actionable evidence links?
  • Can data and configuration be exported through documented APIs?
  • What browser overhead does the SDK add on the devices you support?

Seed known conditions in a test environment: a delayed main-thread interaction, a layout shift, a slow main resource, a cross-origin asset without Timing-Allow-Origin, and an intake rejection. Verify the resulting fields and gaps instead of assuming the product fills them automatically.

Use Backend Logs for the Server-Side Pivot

RUM identifies affected browser experiences. Backend logs can explain the application request when the browser and server share a safe correlation value and the server emits structured evidence. A logs backend is not a substitute for browser RUM, metrics, traces, or Core Web Vitals aggregation.

Fluxtail is a paid Starter/Pro, logs-focused service. It does not provide a RUM browser SDK, session replay, APM, metrics, tracing, or Core Web Vitals dashboards. If an application already writes sanitized correlation, release, route, and error fields into logs delivered through a supported collector or receiver, operators can use documented search and filters, Live Tail, and log alerts to inspect those retained backend events. Field availability depends on the source and mapping; there is no automatic RUM ingestion or browser-to-log correlation.

The durable architecture is therefore a handoff: RUM finds a browser cohort and correlation value; separately collected backend logs provide server evidence. Preserve the boundary, verify the join, and keep each system responsible for the signal it actually measures. For the wider collection lifecycle, see centralized log management and log management best practices.