Query Monitor is a developer panel plugin for WordPress that shows you exactly what happens on every page load: which database queries ran and how long they took, which hooks fired and which callbacks ran on them, which HTTP API calls went out and whether they succeeded, what PHP errors were triggered, and much more. This is not a performance benchmarking tool, it is a real-time execution inspector installed directly in your WordPress admin bar. This tutorial covers every panel in Query Monitor, what data each one shows, and how to use that data to diagnose and fix real problems in WordPress sites.
Installation and Setup
Install Query Monitor from the WordPress plugin directory: search “Query Monitor” in wp-admin Plugins, or install via WP-CLI:
Once active, the Query Monitor toolbar appears in the admin bar for all logged-in administrators. It shows a summary of database queries and page generation time. Click any item in the toolbar to open the full Query Monitor panel.
Viewing Query Monitor on Frontend Pages
Query Monitor shows on all pages, frontend and admin, for users with the view_query_monitor capability. By default, only administrators have this capability. To allow a non-admin user to see Query Monitor output, add the capability to their user:
Authentication Cookie for REST/CLI Access
Query Monitor also outputs data as an X-QM-* HTTP response header set for authenticated requests, making it possible to inspect Query Monitor output on REST API requests and WP-CLI commands where the HTML panel is not visible.
The Queries Panel
The Queries panel is where most developers spend most of their time in Query Monitor. It lists every database query that ran during the page load, with columns for the query SQL, the calling component (which plugin, theme, or WordPress core triggered it), the execution time in milliseconds, and the row count returned.
Reading the Queries Panel
The most important columns are Time (execution time per query), Rows (number of rows returned), and Caller (which function originated the query). The panel also shows the full SQL for each query, hover over a query row to see the expanded SQL with any $wpdb->prepare() substitutions applied.
At the top of the panel, the summary shows total query count and total query time. A typical WordPress page with a handful of active plugins runs 20-40 queries. Pages with WooCommerce, membership systems, or multiple widget areas frequently run 80-150 queries. Anything above 100 queries warrants investigation.
Identifying Slow Queries
Query Monitor highlights rows in red when a query’s execution time exceeds a threshold (default: 0.05 seconds, configurable via the qm/slow-query-threshold filter). Slow queries are the most direct cause of high TTFB on dynamic pages that cannot be fully cached. When you see a highlighted query, expand its SQL and check:
- Does it use a
WHEREclause on an indexed column? Queries without index usage are slow at scale. - Does it use
LIKE '%term%'with a leading wildcard? These cannot use indexes. - Is it scanning the
wp_postmetaorwp_optionstable without an index filter? Both tables grow large and unindexed scans are common on plugin-heavy sites.
Duplicate Queries
The Queries panel has a “Duplicate queries” sub-tab that groups identical SQL statements run multiple times on the same page load. Duplicate queries indicate missing caching in a plugin or theme, the same data is being fetched from the database multiple times when it should be fetched once and stored in a variable or WP object cache. Common causes: a widget that queries for the same post data multiple times, a plugin that calls get_option() in a loop without caching the result, or a theme template part that triggers the same query every time it is included.
Filtering by Component
The Queries panel includes a filter by component, you can view only queries from a specific plugin, the active theme, or WordPress core. This is the fastest way to identify which plugin is responsible for the majority of your query overhead. Enable each plugin’s filter in turn and check its total query time. The plugin with the most total query time is your primary optimization target.
The Hooks Panel
The Hooks panel shows every action and filter that fired during the page load, in execution order. For each hook, it shows which callbacks are attached, their priority, the component they come from, and (for actions) how many times the action fired.
Reading Hook Execution Order
Hooks appear in the order they fired. This lets you verify that your code is hooking into the right point in the WordPress lifecycle. Common issues caught by this panel:
- Your callback is firing at the wrong priority, it runs after the data it needs has been modified by another callback
- A hook that should fire once is firing multiple times (loop pages, AJAX handlers firing on every request)
- Your callback is not appearing at all, the hook name is wrong, the callback is not being registered, or the conditions for registration are not being met
Finding Which Callback Modifies a Value
When a WordPress filter is producing unexpected output, the Hooks panel shows you every callback attached to it and in what order. Search for the filter name, for example, the_content, and you will see every plugin and theme callback that runs on post content, their priorities, and which component they come from. This tells you exactly who is modifying the content and in what order, making it straightforward to identify the callback that is causing the unexpected output.
Hooks Firing During Unexpected Requests
The Hooks panel is also useful for auditing what runs during admin-ajax, REST API, and WP-Cron requests. Plugins that hook into init or wp_loaded without checking the request context run their initialization code on every request, including API requests where their functionality is not needed. The TTFB optimization guide for tweakswp covers how to use this data to audit expensive init hooks and add early bailout conditions to your plugin bootstrap.
The HTTP API Panel
The HTTP API panel shows every outbound HTTP request made by WordPress core, plugins, and themes during the page load. This includes calls to the WordPress.org API, plugin update checks, external payment gateway pings, remote font API requests, license validation calls, and any other wp_remote_get() or wp_remote_post() calls.
What the HTTP API Panel Shows
For each HTTP request, the panel shows the URL, the HTTP method (GET, POST), the response code, the time the request took to complete, whether the response was an error, and which WordPress component initiated the call. The time column is critical: any external HTTP request that takes more than 50ms is a potential TTFB contributor on uncached page loads.
Common Problem Patterns
- WordPress update checks on every request: WordPress checks for plugin/theme updates via cron, but some plugins force this check on every admin page load. Look for requests to
api.wordpress.org/plugins/update-checkwith high frequency. - External font APIs: Requests to
fonts.googleapis.comduring PHP execution (not JavaScript) indicate a plugin or theme is fetching font metadata server-side. Self-host fonts to eliminate this. - Slow third-party APIs: A payment gateway or CRM API that takes 800ms to respond adds that entire latency to your TTFB for any page that calls it synchronously. Move these calls to background processes using
wp_schedule_single_event()or offload to JavaScript. - Failed requests: The panel highlights failed requests (non-2xx status codes or connection errors). Failed external requests often trigger PHP error logs and can cause plugin features to degrade silently.
Blocking Requests in Development
In local development, external HTTP requests fail when you are offline, slow down page loads, and can trigger WordPress to mark plugins as needing updates. You can disable WordPress’s HTTP requests to external hosts entirely on local environments:
This constant disables all wp_remote_* calls to non-localhost URLs. The HTTP API panel will show these requests as blocked, confirming which plugins attempt external calls on every page load.
The PHP Errors Panel
The PHP Errors panel captures and displays all PHP errors that occurred during the page load, regardless of your php.ini error_log configuration. This includes PHP Fatal errors, Warnings, Notices, Deprecated notices, and WordPress-specific _doing_it_wrong() notices.
Error Severity in the Panel
Query Monitor color-codes errors by severity. Fatal errors are shown in red, these stop PHP execution. Warnings in orange, these indicate likely bugs that may produce incorrect behavior. Notices in yellow, these are informational and often point to deprecated function usage or incorrect API calls that work now but may break in future WordPress versions. For a complete guide to PHP error types and how to read stack traces in wp-debug.log, see the WordPress error log reading guide.
_doing_it_wrong Notices
WordPress’s _doing_it_wrong() function triggers notices when WordPress API is used incorrectly, calling get_the_title() outside the loop, registering post types after init, or attaching to hooks that have already fired. These notices do not cause immediate errors but indicate code that may break in future WordPress versions or in edge cases. The PHP Errors panel makes these easy to find and attribute to their source plugin or theme.
The Request Panel
The Request panel shows WordPress’s parsed interpretation of the current URL: which query variables are set, what template was loaded, what conditional tags are true (is_single(), is_archive(), etc.), and which post or taxonomy object was queried.
Template Debugging
The panel shows the exact template file being used to render the current page, the full template hierarchy that was checked before this template was selected, and which theme or plugin provided the file. This is the fastest way to identify which template file is rendering a given page when you are working with complex theme hierarchies or child themes that override parent templates.
Query Variables
The Query Variables section shows both the public query variables (like post_type, cat, tag_id) and the private query variables processed by WordPress for the current request. This is useful when debugging custom rewrite rules or custom query vars, you can verify that your URL is parsing to the query variables you intended, and see exactly what WP_Query received.
The Scripts and Styles Panels
The Scripts and Styles panels list every JavaScript file and CSS stylesheet enqueued on the current page. For each asset, the panel shows the handle, the source URL, its dependencies, whether it was enqueued or only registered, and which component enqueued it.
What to Look For
- Scripts loading on irrelevant pages: A form plugin loading its validation JavaScript on every page, including pages with no forms. Fix with conditional enqueuing.
- Dependency conflicts: If a script declares a dependency that is not registered, WordPress may silently skip loading it. The Scripts panel shows if dependencies are satisfied.
- Duplicate scripts: Two plugins enqueuing different versions of jQuery UI or another shared library. This causes JavaScript errors that are hard to trace without knowing both scripts are loading.
- Footer vs header loading: Scripts loaded in the header block rendering. The panel shows whether each script is enqueued in the header or footer.
Fixing Conditional Enqueuing
When you identify a plugin loading scripts on pages that don’t need them, the fix is to wrap the wp_enqueue_script() call in a conditional check:
The Languages Panel
The Languages panel shows every translation file (.mo file) loaded during the page load, the locale in use, and which textdomains are active. This panel is relevant when:
- Translations are not loading correctly for a plugin or theme
- You are investigating why a string appears untranslated
- Multiple textdomains are loaded for the same locale and you need to confirm the correct file is being used
- A plugin has a textdomain mismatch (the textdomain declared in the plugin header does not match what
load_plugin_textdomain()is using)
Textdomain mismatches are one of the most common causes of untranslatable plugin strings. The Languages panel shows all loaded textdomains, if your plugin’s textdomain is not in the list, it is not loading its translation files correctly.
The Conditionals Panel
The Conditionals panel lists all WordPress conditional tag functions and their return value for the current page. It shows you whether is_single(), is_page(), is_archive(), is_tax(), is_user_logged_in(), is_admin(), and all other conditional functions return true or false for the current request.
This panel is invaluable when debugging code that uses conditionals to control behavior. Instead of var_dump(is_single()) in your template, open the Conditionals panel and see all conditional states at once. It also shows custom conditionals registered by plugins using is_woocommerce() and similar plugin-defined conditional functions.
The Environment Panel
The Environment panel provides a snapshot of the server and WordPress configuration relevant to debugging:
- WordPress version, database version, PHP version
- Active theme and parent theme
- PHP memory limit and current memory usage
- Whether WP_DEBUG, WP_DEBUG_LOG, SAVEQUERIES, and other debug constants are enabled
- MySQL server version and whether MySQL is running on localhost or a remote host
- Whether object caching is enabled and which cache backend is active
- PHP extensions loaded (useful for confirming OPcache, Redis extension, or other required extensions are active)
The Environment panel is the first place to check when something works in local development but not on staging or production, environment differences in PHP version, memory limits, or active extensions explain most cross-environment bugs.
The Capabilities Panel
The Capabilities panel shows every current_user_can() check performed during the page load, which capability was checked, and whether it passed or failed for the current user. This is the correct tool for debugging permission and access control issues.
Debugging User Access Problems
When a user cannot access a feature that they should be able to access, the Capabilities panel shows you every capability check and its result. Find the capability name for the feature in question, for example, edit_posts, manage_woocommerce, or a custom capability, and confirm whether it is returning true or false for the current user. This pinpoints whether the issue is with the capability assignment, the role configuration, or a plugin filtering the capability check result.
The Block Editor (Blocks) Panel
The Blocks panel appears on posts and pages rendered with the Gutenberg block editor. It shows every block present in the current post, its block type, the attributes set on it, and whether it rendered successfully. This panel is useful when:
- A block is failing to render on the frontend while appearing correct in the editor
- A dynamic block’s
render_callbackis throwing errors - You need to verify the attributes passed to a custom block are what you expect
- Investigating block validation failures that cause “Block validation failed” notices in the editor
The Caches Panel
The Caches panel shows object cache statistics: how many cache hits and misses occurred during the page load, the total number of cache operations (gets, sets, deletes), and which cache groups are most active. It also shows the cache backend in use (external persistent cache like Redis, or WordPress’s default internal cache).
Cache Hit Ratio
A high cache miss rate when you have Redis or Memcached configured indicates that data is not being found in the cache on reads, either the cache is not being populated correctly, cache keys are being generated inconsistently, or cache entries are expiring too quickly. A well-configured WordPress site with persistent object caching typically achieves a hit rate above 80% on pages with repeated visitors.
The REST API Panel
The REST API panel appears when the current request is a WordPress REST API request. It shows the matched REST route, the request parameters, which controller handled the request, the response data, and any errors that occurred during the request.
For frontend JavaScript that calls the REST API, you can inspect the REST API panel by adding the qm/enable-rest query parameter to your API request URL in development. This works when you are authenticated and have the view_query_monitor capability. The panel shows the full server-side execution for that API request, including all queries and hooks that fired, giving you the same visibility for REST API debugging as you have for regular page loads.
The Admin Screen Panel
The Admin Screen panel appears only in wp-admin pages. It shows the current admin screen ID, the base, the parent base, and any column information for admin list table screens. Use this panel when registering meta boxes or adding columns to admin screens, it tells you exactly which screen ID and parent to use in your add_meta_box() or manage_{$screen_id}_columns hook calls.
Using Query Monitor Programmatically
Query Monitor provides a logging API that you can use to add custom debug output to its panels. This is useful for logging variable values, execution times, or custom messages from your plugin or theme code during development.
These functions add entries to Query Monitor’s Logs panel, which appears when custom log entries are present. The second argument to do_action('qm/debug'...) accepts a sprintf-style format string as the third argument, or you can pass arrays and objects which Query Monitor renders in an expandable tree.
Query Monitor vs WP_DEBUG: When to Use Each
| Scenario | Use |
|---|---|
| Identify which queries are slow on a given page | Query Monitor Queries panel |
| Find which plugin is causing a JavaScript error in wp-admin | SCRIPT_DEBUG + browser console |
| Capture errors on staging without displaying them | WP_DEBUG + WP_DEBUG_LOG + WP_DEBUG_DISPLAY = false |
| Trace which plugin is filtering post content | Query Monitor Hooks panel |
| Find external HTTP calls that are slowing TTFB | Query Monitor HTTP API panel |
| Get all PHP errors on every page without a plugin | WP_DEBUG |
| Debug a REST API endpoint | Query Monitor REST API panel |
| Check why a translation is not working | Query Monitor Languages panel |
| Audit all init hooks running on a page | Query Monitor Hooks panel |
Extending Query Monitor with Add-ons
Query Monitor supports add-on plugins that add additional panels. Notable add-ons:
- Query Monitor for ACF: Adds an Advanced Custom Fields panel showing field values and query performance for ACF queries
- Query Monitor for WooCommerce: Adds WooCommerce-specific data to the panel (cart sessions, product queries, checkout hooks)
- Query Monitor Extended: Adds memory usage per hook, hook execution time, and expanded backtrace display
Performance and Security Notes
Query Monitor adds negligible overhead to page generation for authenticated admin users, its data collection is designed to be lightweight. The plugin does not run at all for unauthenticated visitors, so there is no frontend performance impact for regular visitors.
Do not worry about Query Monitor being active on production for admin users, the performance overhead from collecting hook, query, and HTTP API data is small compared to the value of being able to diagnose production issues in real-time. The data is never exposed to unauthenticated users, making it safe to leave installed on live sites. That said, review the list of users with the view_query_monitor capability periodically to ensure only authorized developers have access.
Frequently Asked Questions
Does Query Monitor work with page caching?
Query Monitor works only on requests that complete the full WordPress bootstrap. On cached page requests (served by full-page caching plugins before PHP finishes), Query Monitor does not collect data because WordPress does not run. On uncached requests, logged-in admin users, and admin pages (which are always excluded from page caching), Query Monitor shows full data. The admin bar item will not appear on cached frontend pages, this is expected behavior, not a bug.
Why is the Query Monitor toolbar not showing?
Three common reasons: (1) You are not logged in as a user with view_query_monitor capability. (2) The admin bar is disabled, check Show Admin Bar in your user profile settings. (3) A caching plugin is serving a cached version of the page and Query Monitor’s output is not included in the cache. Check in an incognito window after clearing your site’s cache.
Can I use Query Monitor with SAVEQUERIES?
Query Monitor works with or without SAVEQUERIES. Query Monitor hooks into $wpdb using its own mechanism and does not require the constant. Enabling SAVEQUERIES on top of Query Monitor just adds the query data to $wpdb->queries array in addition to Query Monitor’s own storage, it is redundant but harmless. See the complete WP_DEBUG constants guide for the full explanation of SAVEQUERIES and when to use it independently of Query Monitor.
Query Monitor covers the full surface area of a WordPress request, from database queries to hook execution, HTTP API calls to script loading, PHP errors to REST API routing. Install it in your development environment as a permanent tool, not just when debugging specific issues. Running it on every page load surfaces problems you did not know existed: duplicate queries, slow external HTTP calls, unnecessary script loading, and deprecated function usage accumulate silently until they become serious issues. The panels described here give you everything you need to understand what WordPress is actually doing on any given page load.
database query optimization Query Monitor WordPress Debugging WordPress developer tools WP_DEBUG
Last modified: March 30, 2026