Performance monitoring for enterprise WordPress answers three questions: what real visitors experience, whether the next release made it worse, and which layer of the stack is responsible. Each question needs a different instrument. Field data from real users (RUM) answers the first, synthetic tests in the deployment pipeline answer the second, and server-side tooling (Query Monitor, an APM agent, OpenTelemetry traces, the MySQL slow log, Redis statistics) answers the third.
A slow checkout or a rising Interaction to Next Paint (INP) on one market’s site costs revenue and search visibility long before anyone files a ticket. Modern WordPress setups are distributed across a CDN, PHP workers, a database, an object cache and sometimes a separate front end, so “is the server up” stopped being a useful question years ago. This guide describes a monitoring stack for that kind of setup, with every threshold and setting taken from the vendor or standards documentation linked in the text.
RUM vs synthetic monitoring for WordPress
To have a complete view of your ecosystem, you must monitor from two perspectives. They do not compete: each one sees something the other cannot.
Real user monitoring (RUM)
RUM captures the experiences of every single visitor, on their device and their network.
- Device fragmentation: how your site performs on a new iPhone versus a mid-range Android phone on a congested mobile connection.
- Interactivity tracking: every interaction that feeds INP, so you can find the specific UI elements that feel slow to users.
- Edge performance: the actual latency between a visitor and the nearest CDN location, per country.
A single site-wide average hides most of what matters. Split RUM data by device class, connection type, country, template (post, archive, product, cart) and first versus returning visit. Report the 75th percentile, because that is the value Google uses to assess Core Web Vitals. The web.dev Web Vitals article puts the measurement rule like this:
“the 75th percentile of page loads, segmented across mobile and desktop devices”
Source: Web Vitals, web.dev
Synthetic monitoring and uptime checks
Synthetic testing uses scripted browsers to load your pages under controlled conditions.
- CI/CD integration: a Lighthouse run every time a developer pushes a change to your WordPress theme or plugin code.
- Uptime and journey checks: scripted runs of the paths that earn money (“add to cart”, “submit contact form”) from the regions you sell in, on a fixed schedule.
Synthetic checks cannot see real users, so they never replace RUM. Their strength is that the test conditions stay constant. When a synthetic result gets worse, the cause is the code or the infrastructure, not a change in who visited the site that day.
How to collect Core Web Vitals with the web-vitals library
You do not need a heavy third-party script to start. Google’s web-vitals library describes itself as a “tiny (~3K, brotli’d), modular library” and measures each metric the same way Google’s own tools report it. It reads the browser’s performance APIs on the page and hands each result to a callback; navigator.sendBeacon then sends the value to your collector, and the library’s README notes that this method works even while the page is being unloaded.
import { onCLS, onINP, onLCP } from 'web-vitals';
function sendToTelemetry(metric) {
const body = JSON.stringify({
name: metric.name,
value: metric.value,
rating: metric.rating,
delta: metric.delta,
id: metric.id,
page: window.location.pathname
});
if (navigator.sendBeacon) {
navigator.sendBeacon('/api/telemetry/vitals', body);
} else {
fetch('/api/telemetry/vitals', { body, method: 'POST', keepalive: true });
}
}
onCLS(sendToTelemetry);
onINP(sendToTelemetry);
onLCP(sendToTelemetry);The snippet sends pathname only, without the query string. That choice matters for privacy, covered below.
The attribution build of the same library turns a bad number into a ticket someone can pick up. For LCP it reports the selector of the LCP element (target), the image URL where there is one, time to first byte, resource load delay and duration, and element render delay. For INP it reports the element the user interacted with, input delay, processing duration and presentation delay, plus longAnimationFrameEntries, the Long Animation Frame entries that overlapped the interaction in browsers that support that API. “INP is bad on product pages” becomes “the variant selector spends most of its time in one script”.
Why Search Console and PageSpeed Insights are not enough
Search Console and PageSpeed Insights show field data from the Chrome UX Report (CrUX). The CrUX API documentation describes that data as “a 28-day rolling average of aggregated metrics”, updated daily around 04:00 UTC. A regression shipped this morning moves that average slowly, so you learn about it days or weeks after it starts.
Coverage is the second limit. According to the CrUX methodology, a page or origin must be publicly discoverable and “sufficiently popular” to appear in the dataset, and only Chrome users who opted in contribute, on desktop and Android. Chrome on iOS, Android WebView apps and other Chromium browsers such as Edge do not contribute data. A B2B portal behind a login, a low-traffic market site or an audience mostly on iPhones may show little or no CrUX data at all.
CrUX remains the right reference for SEO, because it is the dataset Google reports Core Web Vitals from. For release-day reaction you need your own RUM stream with no averaging window.
How to make real user monitoring GDPR compliant
Every RUM beacon is a request from a visitor’s browser to your collector. If it includes an IP address, a session ID, or a full URL with query parameters (think of an email address in a newsletter link), you are processing personal data. For UK visitors that means UK GDPR, with storage and access on the device (cookies, localStorage) covered by regulation 6 of PECR and enforced by the ICO; for EU visitors, the GDPR and each member state’s rules on terminal equipment.
Keep the beacon lean:
- Collect the metric, not the person: metric name, value, template type, country, device class. You do not need an IP address to compute the 75th percentile of LCP.
- Strip query strings before sending and keep only the path.
- Avoid setting a cookie only to stitch visits together. You lose the split between new and returning sessions, and the telemetry no longer depends on device-storage consent.
- Configure the collector so IP addresses are not stored, or are truncated on arrival.
- If your vendor stores data in the US, check whether an EU or UK data region is available and that a data processing agreement is signed.
This is not legal advice; your data protection officer draws the line for your deployment.
How to block slow releases with Lighthouse CI
Lighthouse CI runs Lighthouse against preview or staging URLs in your pipeline and compares the results with assertions you define. By default lhci collect runs Lighthouse three times per URL (numberOfRuns), which smooths out single noisy runs. Each assertion has a level: warn prints the result without failing, while error makes the command exit with a non-zero code, which is what fails the build.
The lighthouse:recommended preset is a reasonable starting point. According to the Lighthouse CI documentation, it asserts that every audit outside performance received a perfect score and warns when metric values drop below a score of 90. You switch individual audits off where they do not fit the project. Add explicit limits for the things your team controls directly, such as total JavaScript and CSS weight on the templates that earn money.
Lighthouse runs in a lab, so its numbers will not match CrUX. Use it to compare one build with the previous one, under the same conditions, not to predict field values.
Visual regression testing with Playwright
Performance is also stability. A plugin update that suddenly shifts the layout on mobile hurts CLS and conversions at the same time.
Playwright ships this check as the toHaveScreenshot assertion. On the first run it generates reference screenshots; later runs compare against them. After a deliberate design change you regenerate the baseline with npx playwright test --update-snapshots. The maxDiffPixels option sets how many pixels may differ before the test fails. The Playwright documentation also warns that rendering varies with host OS, browser version, settings and hardware, so generate baselines in the same environment where the tests run, usually the CI container, and not on a developer laptop.
The practical setup is modest: screenshots of the templates that earn money (home, category, product, cart, checkout) at three viewport widths. Small diffs within the tolerance pass; larger ones wait for a human decision in the pull request.
How to use Query Monitor and Site Health for WordPress diagnostics
Query Monitor by John Blackbourn is the standard diagnostic plugin for a single request. For each page load it lists database queries and flags slow, duplicate or erroneous ones, shows PHP errors with the responsible component and call stack, and lists HTTP API requests with response code, responsible component and time taken. It also covers hooks and actions, template hierarchy, enqueued scripts and styles, and transient updates. By default its output is visible only to Administrators on single sites and Super Admins on Multisite; an authentication cookie can extend access to other users.
We use Query Monitor on staging for diagnosis, not as continuous production monitoring: it explains one request in depth, while an APM agent aggregates all of them.
Site Health (Tools, Site Health) adds a free early warning layer. Its Status tab groups findings into critical issues, recommended improvements and passed tests. Since WordPress 6.6, Site Health reports a critical issue when the total size of autoloaded options exceeds 800 KB, as described in the WordPress 6.6 dev note on autoloaded options. The same release stops autoloading individual options larger than 150,000 bytes by default, a separate limit controlled by the wp_max_autoloaded_option_size filter. Bloated autoload data is loaded on every request, so this check is worth reading after every plugin audit.
WordPress APM with the New Relic PHP agent
An APM agent records every request in production and shows where the time went. The New Relic PHP agent’s WordPress support is a good example of what WordPress-aware instrumentation looks like:
newrelic.framework.wordpress.hooksis enabled by default from agent version 5.3; setting it tofalsedisables the WordPress-specific metrics.- Hook metrics show time spent within each WordPress hook, measured from
apply_filters,apply_filters_ref_array,do_actionanddo_action_ref_array. - Plugin and theme metrics show time spent within each plugin and theme, which answers “which plugin made this page slow” without guessing.
- Since agent 10.16,
newrelic.framework.wordpress.hooks.optionscontrols how much is recorded. Inthresholdmode the agent records only hooks slower thannewrelic.framework.wordpress.hooks.threshold, which defaults to 1 ms. That keeps overhead and data volume down on busy sites.
Datadog, Tideways and Blackfire cover similar ground with their own agents. The choice usually depends on what the rest of the organisation already runs, because one tool across all services beats a better tool nobody else reads.
How to trace WordPress with OpenTelemetry PHP
OpenTelemetry is the vendor-neutral option: the same traces can go to Grafana Tempo, Jaeger or a commercial backend. The PHP auto-instrumentation documentation requires PHP 8.0 or higher and the extension installed with pecl install opentelemetry. Configuration is through environment variables:
OTEL_PHP_AUTOLOAD_ENABLED=true
OTEL_SERVICE_NAME=wordpress-shop
OTEL_TRACES_EXPORTER=otlp
OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318For WordPress there is a dedicated package, open-telemetry/opentelemetry-auto-wordpress, installed with composer require open-telemetry/opentelemetry-auto-wordpress. WordPress core does not use Composer, so the package README shows how to load the Composer autoloader before WordPress starts, for example with php_value auto_prepend_file /var/www/vendor/autoload.php in Apache. The instrumentation can be switched off again with OTEL_PHP_DISABLED_INSTRUMENTATIONS=wordpress.
For one-off profiling from the terminal, WP-CLI’s wp profile command needs no plugin. Install it with wp package install wp-cli/profile-command, then use wp profile stage to time the bootstrap, main query and template stages, wp profile hook for actions and filters, and wp profile queries for database queries. It works on servers where you are not allowed to install a plugin. Xdebug gives the most detailed profile of all, and its overhead keeps it on local machines.
Which database and object cache metrics to monitor
The MySQL slow query log is disabled by default. It records statements that take longer than long_query_time seconds and examine at least min_examined_row_limit rows. The default long_query_time is 10 seconds, far too lax for a web request; the minimum is 0 and the value accepts microsecond resolution. Each entry carries Query_time, Lock_time, Rows_sent and Rows_examined, and a large gap between rows examined and rows sent is the classic sign of a missing index, typically on wp_postmeta behind product filters. log_queries_not_using_indexes adds unindexed queries to the log, and log_throttle_queries_not_using_indexes limits how fast they are written. mysqldumpslow summarises the log file. More on fixing what the log finds is in our WordPress database optimization guide.
For a Redis object cache, the INFO command provides the raw counters:
keyspace_hitsandkeyspace_misses: successful and failed key lookups. The hit ratio is hits divided by hits plus misses.evicted_keys: keys removed because of themaxmemorylimit. A rising count means the cache is too small for the working set.mem_fragmentation_ratio: the ratio betweenused_memory_rssandused_memory.
There is no universal “good” hit ratio, because it depends on how much of the site is cacheable. Record your own baseline after a clean deployment and alert on a sustained drop: it usually means a new plugin bypasses the cache, a configuration changed, or traffic shifted to uncached pages.
How to monitor headless WordPress
When WordPress is only the content API and the front end runs on Astro or Next.js, there are two systems to watch. On the backend: response time and 5xx rate of the REST API or WPGraphQL endpoints. On the front end: build time, page regeneration time and client-side hydration cost.
A distributed trace ties the two together. The W3C Trace Context recommendation defines the traceparent and tracestate headers that carry a trace ID from the front end to the WordPress API, and OpenTelemetry supports them for context propagation. One request is then visible across the CDN, the front end, the WordPress API, MySQL and Redis. Without it, each team sees only its own segment and every slow request looks like the neighbouring layer’s fault.
Anomaly detection for website performance
Fixed thresholds such as “alert me if LCP is above 2.5 s” either fire too late or produce false alarms during traffic peaks. Dynamic baselines learn what is normal for each hour and weekday and report deviations before a hard limit is reached. Most APM and observability platforms offer this; the setting matters less than the context next to it.
An anomaly is only useful when you can see what changed next to it. Mark deployments, plugin updates and campaign launches on the same timeline as the metrics: Grafana does this with annotations, New Relic with deployment markers. A TTFB spike sitting directly beside a plugin update ends the investigation before it starts.
Setting a performance budget for Core Web Vitals
A budget is a set of upper limits where crossing one has an agreed consequence. Google’s “good” thresholds at the 75th percentile are the floor: LCP within 2.5 s, INP within 200 ms, CLS within 0.1, per web.dev. Enterprise teams usually set stricter targets per project, plus limits on shipped JavaScript and CSS checked in the Lighthouse CI gate.
| Metric | Reference value | When the budget is exceeded |
|---|---|---|
| LCP | 2.5 s (“good” at p75) | Deployment blocked |
| INP | 200 ms (“good” at p75) | Alert to the front-end team |
| CLS | 0.1 (“good” at p75) | Layout review |
| TTFB | 0.8 s rough guide, not a Core Web Vital (web.dev) | Infrastructure investigation |
Budgets without an owner decay. Name one person who reviews the trend weekly, decides whether a threshold still makes sense, and can say no to a release. Route alerts by severity too: a broken checkout pages the on-call engineer, a slow TTFB creep goes to a team channel, and minor drift waits for the weekly report. If every alert wakes someone up, after a month nobody reads them.
How page speed affects conversion rate
The useful question for management is not “how many milliseconds” but “what does a slower page cost us”. Answer it with your own data rather than a percentage lifted from someone else’s case study. Send the RUM values into the same analytics store as your conversion events, bucket sessions by LCP or INP band, and compare conversion per bucket. The result is specific to your audience and your checkout, and it is the version a finance director will accept when you ask for a performance sprint.
What a WordPress monitoring service includes
At WPPoland, monitoring work covers three things.
- Full-stack monitoring design: RUM, synthetic checks and APM or OpenTelemetry traces wired into one view, so one incident is visible from the browser down to the SQL query.
- Regression prevention: the Lighthouse CI and Playwright gates that stop slow or broken templates before they reach production.
- Reporting people can act on: metrics translated into the decisions management has to make, with an owner for each budget.
Tooling and implementation are priced individually, because cost depends on the number of sites, traffic volume and the chosen vendor.
Where to start with WordPress performance monitoring
Start small and in order. First, field data from real users with the web-vitals library, because it shows what visitors actually experience, and Query Monitor on staging. Next, Lighthouse CI and Playwright checks in the pipeline, plus synthetic journey checks from the regions you sell in. Then APM or OpenTelemetry traces and the slow query log on the endpoints the first two layers point to. Each layer answers a question the previous one raises, and an alert that nobody owns is noise.
When the monitoring shows where the time goes, the fixes follow. Our WordPress speed optimization guide covers the server, caching and front-end work, and the INP optimization guide goes deeper into the interactivity metric. For a hands-on review of your site, see WordPress speed optimization at WPPoland.







