Website Performance Monitoring Tools: A Practical Guide to RUM, Synthetic Checks, and Lab Testing
Learn how to combine real-user data, synthetic checks, and lab tests to monitor availability, speed, Core Web Vitals, and user journeys.
Website performance monitoring is a measurement practice, not one score or one product category. Use real-user monitoring (RUM) to learn what visitors actually experienced, synthetic checks to test availability and important workflows on a schedule, and lab tools to reproduce problems and compare releases. A reliable program combines all three.
Field data and synthetic data answer different questions. A page can score well in a controlled Lighthouse run and still be slow for visitors on older phones or congested networks. A ping can be healthy while checkout is broken. A scheduled browser journey can catch that workflow failure, while RUM shows how widely it affected users.
1. Choose the right measurement for the question
| Question | Best first signal | What it tells you | What it cannot tell you alone |
|---|---|---|---|
| Are visitors experiencing slow pages? | RUM and field data | Actual browser timings, Web Vitals, devices, regions, and user segments | Why a particular request or release caused the problem |
| Is the site reachable from important locations? | HTTP or ping check | Reachability, status, and response time | Whether the page renders correctly or a user task works |
| Does a page render expected content? | Simple browser check | JavaScript execution, assets, CDN behavior, and visible content assertions | How every real visitor experienced it |
| Does sign-in, search, or checkout work? | Scripted browser journey | Multi-step workflows, including authenticated paths | The full distribution of customer experiences |
| Did a code change regress performance? | Lab test in CI | Repeatable comparisons under controlled conditions | Production variability and long-tail users |
Google describes CrUX-based reports as aggregate field context from Chrome users. PageSpeed Insights and Search Console generally report a recent 28-day window, while the CrUX dataset and dashboard use calendar-month organization. Google recommends adding your own RUM for more immediate and detailed feedback. See Google’s field performance guidance.
2. Monitor real users with RUM
RUM runs in the visitor’s browser and records what happened during actual page views. Track the distribution of experiences instead of a single average. A median can hide a slow tail caused by a particular device, connection, country, browser, or release.
Core Web Vitals to track
- LCP (Largest Contentful Paint): a loading metric.
- INP (Interaction to Next Paint): an interactivity metric.
- CLS (Cumulative Layout Shift): a visual-stability metric.
Report the data source, collection window, and segments with every dashboard. Google uses the percentage of visits meeting the current “good” threshold for each metric; check the current Web Vitals documentation before publishing numeric thresholds because they can change.
Collect Web Vitals with JavaScript
<script type="module">
import {
onCLS,
onINP,
onLCP
} from 'https://unpkg.com/web-vitals@4/dist/web-vitals.attribution.js?module';
const send = (metric) => {
navigator.sendBeacon('/rum', JSON.stringify({
name: metric.name,
value: metric.value,
rating: metric.rating,
id: metric.id,
url: location.href,
userAgent: navigator.userAgent
}));
};
onCLS(send);
onINP(send);
onLCP(send);
</script>
Send only the fields you need, document consent and retention, and avoid collecting secrets or unnecessary personal data. Segment dashboards by template, device class, browser, country, release, and connection where those dimensions are useful and lawful.
3. Add synthetic availability and browser checks
Synthetic monitoring runs the same request or journey at a chosen interval and from selected locations. It gives you a repeatable baseline before a release reaches users, during low-traffic periods, and in regions where RUM volume is small.
Start with an HTTP check
curl --fail --max-time 20 -sS -o /dev/null \
-w 'status=%{http_code} total=%{time_total}s\n' \
https://example.com/health
Use a dedicated health endpoint when possible. Assert the status, a response-time limit, and a small expected body value. A ping detects reachability and latency; it does not prove that JavaScript rendered, links work, or checkout succeeds.
Python API check
import time
import requests
url = "https://example.com/api/health"
started = time.perf_counter()
response = requests.get(url, timeout=10)
elapsed = time.perf_counter() - started
response.raise_for_status()
body = response.json()
if body.get("status") != "ok":
raise RuntimeError(f"Unexpected health payload: {body}")
if elapsed > 2:
raise RuntimeError(f"Health check exceeded 2 seconds: {elapsed:.3f}s")
print({"status": response.status_code, "seconds": round(elapsed, 3)})
Node.js API check
const started = performance.now();
const response = await fetch('https://example.com/api/health', {
signal: AbortSignal.timeout(10_000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const body = await response.json();
if (body.status !== 'ok') throw new Error(`Unexpected payload: ${JSON.stringify(body)}`);
const seconds = (performance.now() - started) / 1000;
if (seconds > 2) throw new Error(`Too slow: ${seconds.toFixed(3)}s`);
console.log({ status: response.status, seconds });
Browser checks and journeys
A simple browser check loads the page in Chrome or Firefox, executes JavaScript, downloads assets, and can assert that expected text or elements appear. A scripted browser journey chains actions such as sign-in, search, adding an item, and checkout. Keep journeys focused on revenue, trust, and support-critical paths: they cost more to run and require maintenance when the UI changes.
import { chromium } from 'playwright';
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({
viewport: { width: 1366, height: 768 }
});
try {
await page.goto('https://example.com', { waitUntil: 'networkidle', timeout: 30_000 });
await page.getByRole('heading', { name: /welcome/i }).waitFor();
await page.screenshot({ path: 'synthetic-home.png', fullPage: true });
} finally {
await browser.close();
}
4. Use lab tools for reproducible debugging
Lab tests run in a controlled environment. Run Lighthouse or WebPageTest locally, in CI, and before releases to reproduce regressions and compare changes. Record the URL, browser version, viewport, throttling profile, build identifier, and test date so results are comparable.
npx lighthouse https://example.com \
--preset=desktop \
--output=html \
--output-path=./lighthouse-report.html
Do not treat a lab score as a complete user-experience report. Pair it with field distributions and synthetic checks.
5. Design alerts that lead to action
| Signal | Example alert | Owner and response |
|---|---|---|
| Availability | Two consecutive failures from one or more critical locations | On-call engineer checks deploys, DNS, TLS, origin, and dependencies |
| Latency | p95 response time above the service objective for 10 minutes | Service owner inspects traces, database, cache, and upstreams |
| Browser journey | Checkout assertion fails in two locations | Product and engineering reproduce the exact step and recent UI changes |
| RUM | Good-experience percentage drops for a key template or release | Web performance owner segments by device, region, browser, and release |
| Lab regression | Budget exceeded in CI compared with the baseline | Change author inspects bundles, images, layout, and long tasks before merge |
Use consecutive failures, sensible windows, maintenance periods, and recovery notifications to reduce noise. Store check history long enough to compare releases, seasonal traffic, and incidents.
6. Compare monitoring tools before buying
Compare every candidate against the same workload. Ask:
- Measurement type: RUM, CrUX or PageSpeed Insights data, lab tests, synthetic checks, or a combination?
- Coverage: required pages, APIs, devices, browsers, locations, and private applications?
- Browser fidelity: full assets and JavaScript, visible-content assertions, and interactions, or only an endpoint request?
- Debugging: browser errors, traces, backend services, and release correlation?
- Alerting and history: thresholds, deduplication, maintenance windows, and trend views?
- Setup: JavaScript instrumentation, browser agents, API keys, custom scripts, and CI integration?
- Cost: pages × locations × frequency, plus scripted journeys and data volume?
- Privacy: what session data is collected, retained, exported, and subject to consent or regional rules?
There is no universal best platform in the reviewed evidence. New Relic is a documented example: its pages describe synthetic checks for availability, Core Web Vitals, page and content load, broken links, and SSL validity, plus browser monitoring and APM. Its Core Web Vitals monitor requires a Google PageSpeed Insights API key. Treat current quotas and prices as volatile and verify them before purchase.
7. Reliability, performance, and cost considerations
Reliability
- Run checks from more than one location before declaring a global outage.
- Retry transient network failures carefully, but preserve the first failure for diagnosis.
- Separate dependency failures from your own origin with explicit assertions and health endpoints.
- Version browser scripts and test data; alert when a check itself is stale.
- Use synthetic checks for early warning and RUM for impact confirmation.
Performance overhead
RUM adds client-side work and network traffic. Load instrumentation asynchronously, sample high-volume events when appropriate, and use sendBeacon so reporting does not block navigation. Synthetic browsers consume CPU, bandwidth, and concurrency; schedule expensive journeys at a frequency that matches their operational value.
Cost planning
Estimate monthly volume as checks × locations × runs, then add browser journeys, RUM event volume, retention, and overage. A low-frequency ping is cheap but narrow. A high-fidelity checkout journey is more expensive but tests a business-critical path. Recalculate after adding regions or increasing frequency.
8. Troubleshooting common failures
| Symptom | Likely cause | Fix |
|---|---|---|
| Ping passes but users report a broken page | Only reachability was tested | Add a browser assertion for rendered content and a journey for the affected task |
| RUM shows slow INP but lab is normal | Real users have different devices, scripts, or interactions | Segment by device, browser, template, and release; reproduce with a representative profile |
| Synthetic failures occur in one region | Regional DNS, CDN, routing, or dependency issue | Compare locations and inspect provider and origin logs before paging globally |
| Browser check times out at network idle | Long polling, analytics, or third-party requests never become idle | Wait for a specific selector or response and set a bounded timeout |
| Checks fail after a UI release | Selectors or test data changed | Use stable test IDs, version scripts with the application, and refresh fixtures |
| Alerts are noisy | Thresholds are too tight or single failures page | Require consecutive failures, use rolling windows, and add maintenance suppression |
| CrUX and RUM disagree | Different populations, windows, sampling, or page groupings | Label each dataset and compare like-for-like URLs, segments, and periods |
| Lab results vary between runs | Uncontrolled CPU, network, cache, or third-party conditions | Pin the environment, clear or warm caches consistently, and compare multiple runs |
9. Or skip the browser setup
If your immediate need is a clean page image for a dashboard, report, visual check, or agent workflow, ScreenshotNeo provides a single screenshot request. Read the ScreenshotNeo API documentation for all options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Cookie and consent banners, newsletter popups, and chat widgets are removed before capture. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed; response headers report the page verdict and billing status. An MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
10. Implementation checklist
- List the pages, APIs, and user journeys that matter.
- Add RUM and label its population, window, and privacy controls.
- Track LCP, INP, and CLS distributions and segment the slow tail.
- Create HTTP checks for reachability and response time.
- Add browser assertions for critical rendered content.
- Automate one or two revenue or trust-critical journeys.
- Run controlled lab tests in CI and record the environment.
- Define alert thresholds, consecutive-failure rules, owners, and runbooks.
- Review volume, retention, and regional coverage against budget.
- Reconcile synthetic incidents with RUM impact after every outage.
FAQ
Can a website be fast in Lighthouse but slow for real visitors?
Yes. Lighthouse is a controlled lab test. Real visitors use different devices, networks, browsers, locations, and extensions. Use RUM to measure their experience.
Is a ping enough for website monitoring?
No. It verifies reachability and response time. Add browser checks and scripted journeys to test rendering and tasks.
How often should synthetic checks run?
Choose a frequency based on the cost of delayed detection and the cost of each run. Critical workflows usually need more frequent checks than informational pages.
Should I monitor every page?
Start with representative templates, high-traffic pages, and revenue or support-critical workflows. Expand when incidents or RUM segmentation show a gap.
Do Core Web Vitals measure all of website performance?
No. They cover loading, interactivity, and visual stability. Availability, API latency, errors, accessibility, and workflow success need additional checks.
What should a performance dashboard show?
Show field distributions and good-experience percentages, synthetic availability and latency by location, journey success, lab trends by release, active alerts, and links to ownership and runbooks.


