Skip to main content

RUM

Real user monitoring captures what actual browsers experience on your sites: Core Web Vitals, page views, and JavaScript errors. On hosts running the APM layer it is fully configless: the agent injects the browser snippet and receives the beacons itself, with nothing to add to the site.

RUM is opt-in per site. Until a site is enabled, nothing is injected and no beacon is accepted. An empty opt-in list means the whole feature is inert.

How it works on an APM host​

The same PHP prepend that powers APM does the browser half:

  • Injection. On an enabled site, the prepend inserts a small vitals snippet into HTML responses. It only touches GET responses of type text/html that contain a </head>, and on any doubt it returns the page byte-identical. If the page already carries the hosted RUM snippet, injection yields, so a site can never be counted twice.
  • Beacons. The snippet reports back to the same origin (a POST with a ?wkr-rum=1 marker, so it works under any routing). The prepend validates the site against the opt-in list, accepts the beacon only when the browser says it came from the page itself (a same-origin request; a beacon forged from another site or posted by a script elsewhere is dropped before it costs anything), relays it to the agent on loopback, and answers the browser immediately.
  • Caps. The agent accepts at most 6000 beacons a minute per site and 60 a minute per visitor address, so a bot hammering a page cannot inflate the views you are billed for; the visitor address is used for that count on the host and never stored.
  • Upstream. The agent forwards beacons over its one outbound WebSocket, like every other signal.

The site opt-in list arrives from the server with the agent's configuration. Enabling a site in the console reaches the host within about a minute, with no reload of anything.

What is collected​

  • Core Web Vitals (LCP, CLS, INP and friends) and page timings such as TTFB.
  • Page views, per path.
  • JavaScript errors, including unhandled promise rejections. Rejections carry the top stack frames, because browsers give them no source location on their own.
  • Frustration signals: rage clicks (the same element hammered in quick succession) and error clicks (a click immediately followed by a JavaScript error), counted per page view with the selector of the element that took the abuse.
  • Breadcrumbs on error: when a JavaScript error fires, the beacon carries the last structural events that led to it, recent navigations and network requests with method, path, duration and, where the browser exposes it, the HTTP status.

Frustration signals and breadcrumbs​

These answer "what was the visitor actually fighting with" without recording the session: no DOM capture, no keystrokes, no screen content, just counters, selectors and a short structured trail. Everything is validated and capped at ingest (signal names against an allow list, selector length, counter ceilings, trail size), and a beacon that arrives with a broken trail loses the trail, never the page view.

Honest limits: the HTTP status inside breadcrumbs comes from a browser API that Chromium and Firefox expose and Safari (including everything on iOS) does not, so crumbs from those browsers carry the request without its status. Cross-origin requests without Timing-Allow-Origin report no status either. Route changes done purely through the history API in a single-page app are not tracked as navigations, real navigations and back-forward moves are.

Privacy​

The visitor's IP address is used once, at ingest, to resolve a country, and is then dropped. No address is stored anywhere. What remains is the country code, the browser and device class, the path, the timings and the errors.

Correlation with APM​

On instrumented sites the page response carries the trace identity, and the beacon reports it back. A slow page seen by a real visitor links to the exact server-side trace of that request, so you land in the waterfall of what actually happened. Browser support for reading the header varies (Chromium exposes it, others may not), so trace coverage is partial by nature.

Sites without the agent​

A site that does not run on an APM host (a third-party host, or a PHP version below the APM matrix) can still be monitored with the hosted snippet: one <script> tag pointing at Wakora's collect endpoint, added to the page by hand. The console shows the exact tag when you add such a site. Everything else (vitals, views, errors, privacy handling) works the same. If the site later moves onto an APM host, the injected path takes over by itself and the manual tag can be removed whenever convenient.

Requirements and limits​

  • Injection needs the APM layer-2 runtime active on the site's PHP version (see APM). On PHP versions outside the matrix, use the hosted snippet.
  • The snippet is a few KB, loads async, and never blocks rendering.