A client’s WooCommerce admin had become slow over a few months. Every product edit took several seconds to save. They had already tried the usual steps: a caching plugin, a larger server, disabling plugins one by one on a busy live store, which was stressful and inconclusive. When we connected a profiler, the answer took about ten minutes to find. A shipping rate plugin was calling an external API on every admin page load, waiting for a response that took two seconds, and doing it three times per request. It’s the fastest way to profile WordPress problems that caching hides.
That’s the value of profiling. Instead of guessing, deactivating and hoping, you measure exactly where PHP spends its time. When you profile WordPress, you see which functions, plugins, hooks and queries are responsible, down to the millisecond. This article covers three tools that between them handle almost every WordPress performance investigation: Query Monitor, SPX and Xdebug.
When profiling matters
Page caching hides slow PHP for anonymous visitors on cached pages. Profiling matters for everything the cache doesn’t cover:
- Admin pages and the block editor
- WooCommerce cart, checkout and account pages
- Logged-in users on membership and learning sites
- AJAX and REST API requests
- Cron jobs and background processing
- The first uncached request for any page
If those feel slow, the problem is in PHP execution or the database, and a profiler is the right tool.
Tool 1: Query Monitor
Query Monitor is a free WordPress plugin and the best starting point for most investigations. It adds a panel to the admin bar showing, for the current page:
- Total page generation time and memory usage
- Every database query, with time, caller and component
- Duplicate queries
- Slow queries
- HTTP API calls, including external requests and their duration
- Hooks and actions that ran
- PHP errors and notices
- Scripts and styles enqueued
- Template files used
Install it, load a slow page while logged in as an administrator and open the panel. Start with the overview, then check:
- Queries by Component, which shows total query time per plugin or theme
- HTTP API Calls, where slow external requests often hide
- Slow Queries, highlighted automatically
Query Monitor doesn’t measure time spent in PHP code outside queries and HTTP calls in detail. If page generation takes three seconds but queries only account for 0.4 seconds, the rest is spent running PHP, and you need a profiler for that.
Remove or deactivate Query Monitor on production when you’re finished. It’s safe, but it adds overhead.
Tool 2: SPX
SPX is a lightweight PHP extension for profiling, designed to be easy to use with a built-in web interface. It shows flame graphs, timelines and function-level statistics, and it has low enough overhead that you can use it selectively on a staging server, or carefully on production for short periods.
Installing SPX
SPX is installed as a PHP extension. On a Linux server, you typically build it from source:
Then enable it in a PHP configuration file for your PHP-FPM version:
Restart PHP-FPM. The http_key and IP allowlist protect the web interface, which you must never leave open to the public.
Profiling a request
Visit any page on the site with SPX’s control panel enabled, at a URL like:
The control panel lets you enable profiling for your browser via a cookie. Then load the slow page normally. Back in the SPX interface, you’ll see the recorded request. Open it for:
- A flame graph, where wide bars represent functions that took the most time, stacked to show what called what
- A timeline, showing how time was spent during the request
- A function list, sortable by inclusive and exclusive time
The flame graph is the quickest way to spot problems. Look for wide bars that belong to plugin functions. Follow them down to see the specific calls. In WordPress, you’ll often see time attributed to do_action or apply_filters; expand those to find which callback is responsible.
Tool 3: Xdebug
Xdebug is the most widely known PHP debugging extension, offering step debugging, stack traces and profiling. Its profiler writes detailed output files in cachegrind format, which you open in tools such as QCachegrind, KCachegrind or Webgrind.
Xdebug has much higher overhead than SPX, especially with profiling enabled, so it’s best used on local development or staging, never on busy production servers.
Enabling profiling
In Xdebug 3, configure it like this:
With start_with_request set to trigger, profiling only happens when you add a trigger to the request, such as the XDEBUG_TRIGGER query parameter or cookie:
The profiler writes a cachegrind file to the output directory. Open it in QCachegrind to see a call graph and function list with time spent in each function.
Xdebug’s strength is detail: exact call counts, call graphs and the ability to combine profiling with step debugging when you need to understand why code behaves the way it does.
Commercial profilers
Hosted profiling tools such as Blackfire, Tideways and New Relic provide continuous or on-demand profiling with polished interfaces, comparisons between runs and production-friendly overhead. They cost money but save time on larger projects and agencies managing many sites. Many managed WordPress hosts integrate with one of them.
Reading a WordPress profile
WordPress profiles look complicated at first because so much happens through hooks. A few tips:
- Start with the widest bars or highest inclusive time. That’s where most time goes.
- Expand do_action and apply_filters to see individual callbacks. The plugin name usually appears in the function or file path.
- Look for external calls, such as curl_exec, file_get_contents with URLs or wp_remote_request. Waiting on remote servers is a very common cause of slow pages.
- Look for repeated calls. A function called 5,000 times can be a problem even if each call is fast.
- Check database time. Time in mysqli_query or wpdb::query points back to queries, which Query Monitor helps you analyse.
- Compare with and without a plugin on staging to confirm the impact.
Common findings
From profiling many WordPress sites, these causes come up again and again:
- Plugins calling external APIs on every request, without caching the result
- Translation files being loaded and parsed for large plugins on every request
- Expensive calculations repeated many times instead of cached
- Plugins running admin-only code on the front end
- Heavy autoloaded options being unserialised
- Slow meta queries from filtering features
- Page builders processing large layouts for every uncached request
Each has a different fix, but they’re all easy to see in a profile and almost impossible to find by guessing.
A practical workflow
- Reproduce the slow request on staging with a copy of production data.
- Use Query Monitor to check queries and HTTP calls.
- If PHP time dominates, profile with SPX or Xdebug.
- Identify the top two or three contributors.
- Fix, replace or configure the responsible plugin or code.
- Profile again to confirm the improvement.
When you profile WordPress this way, performance work stops being trial and error. You fix the real problem first, and you can show clients exactly what changed.
Profiling background work
Not every slow request is a page view. Cron jobs, imports, scheduled actions and REST API calls can be just as expensive, and they often run when nobody is watching. To profile them, trigger the work manually on staging while the profiler is active, for example by running a specific cron event with WP-CLI or calling a REST endpoint with the profiling trigger included. With Xdebug, setting the trigger as an environment variable lets you profile WP-CLI commands directly.
Background work matters because it competes with visitors for PHP workers and database time. A nightly synchronisation job that runs for ten minutes and saturates the database can make the whole site slow during that window, which shows up as mysterious performance dips at the same time every day.
Keeping a performance baseline
Once you’ve fixed the major problems, record a baseline: generation time and memory for a handful of key pages, such as the homepage uncached, a product page, the cart, the checkout and a heavy admin screen. Re-measure after major updates or new plugins. A sudden jump tells you exactly when a regression appeared, which makes the next profiling session much shorter.
Frequently asked questions
Can I profile on shared hosting?
Usually not with SPX or Xdebug, because they require installing PHP extensions. Query Monitor works anywhere. For deeper profiling, copy the site to a local environment or a VPS.
Is it safe to profile production?
SPX and commercial profilers can be used carefully on production for short periods. Xdebug profiling is too heavy for busy production servers.
Why does the profile show most time in WordPress core?
Core functions like do_action run plugin code. Expand them to see which callbacks are responsible.
How fast should an uncached WordPress page be?
As a rough guide, under 500 milliseconds of server time for typical pages, and under a second for complex WooCommerce or admin pages.




