PerformanceUpdated 8 min read
How to fix a slow WordPress admin dashboard without guesswork
Fix slow WordPress admin screens by timing requests, tracing queries, checking cron, AJAX, autoloaded options, HTTP calls, and memory.
By Hamza Ahmad AslamAI & WordPress Developer | Speed Optimization & Site Security

On this page
- How to fix slow WordPress admin issues with a repeatable baseline
- How to use Query Monitor to find the blocking work
- Read the panels in this order
- Trace a slow query to its caller
- Treat an expensive query at its source
- How to measure autoloaded options on WordPress 6.6 and later
- How to identify admin-ajax and Heartbeat requests
- How to inspect Action Scheduler and WP-Cron
- How to spot remote HTTP calls that delay wp-admin
- How to separate memory pressure from PHP worker exhaustion
- How to verify that the fix worked
- What to do next
- Frequently asked questions
Short answer: To fix slow WordPress admin screens safely, time one repeatable request, profile the work inside that request, then change one cause at a time. Query Monitor, browser Network data, WP-CLI, and the database can show whether the delay comes from SQL, AJAX, scheduled jobs, remote HTTP calls, or memory pressure.
How to fix slow WordPress admin issues with a repeatable baseline
Start with one admin screen that is consistently slow. Record its exact URL, the logged-in user role, and the action you take before timing it. A WooCommerce orders screen, an editor load, and a settings save can execute very different code.
Open the browser Network panel, reload the screen, and record the duration of the main document request. The Chrome DevTools Network reference (opens in a new tab) explains how to inspect request timing. Treat this as an end-to-end baseline because it includes more than PHP execution. Then repeat the same process on an admin screen that feels fast.
Keep the comparison inside wp-admin and under the same account. A fast public page does not prove the admin path is healthy. A page cache can satisfy public requests without running the PHP and SQL used by an authenticated admin request.
Record the baseline before installing profiling tools. Use a small table in your notes with the URL, role, action, request duration, and any visible error. You will use that same request after every change.
How to use Query Monitor to find the blocking work
Install Query Monitor from its official project (opens in a new tab), reproduce the slow request, then read its panels in a fixed order. Query Monitor records database query time, responsible components and calling functions, PHP errors, and WordPress HTTP API requests.
Read the panels in this order
Start with the Overview. Compare total page generation time, database query time, query count, and memory use with the fast admin screen. Do not invent a universal threshold. The useful signal is the difference between repeatable requests on the same site.
Next, inspect database queries grouped by component. This shows whether WordPress core, a plugin, or the active theme owns most of the query work. Then inspect the slow and duplicate query views or filters for individual statements that deserve attention.
After SQL, open HTTP API Requests. Look for remote requests whose elapsed time is a meaningful part of the page request. Finish with PHP Errors. Warnings, notices, and deprecated calls can reveal broken code paths even when they are not the main timing cost.
Trace a slow query to its caller
Query Monitor records the calling function and responsible component for each database query. Its database collector also records a stack trace when the Query Monitor database drop-in is active.
Open the slow query, identify its component, then inspect the caller or stack trace. Follow the first useful frame into plugin or theme code. That connects the SQL statement to the callback or method that issued it, rather than blaming a table because it happens to be large.
If several slow statements belong to one component, filter the query list by that component. Reproduce the same screen again after disabling or correcting only that code path on a safe staging copy.
Treat an expensive query at its source
A pattern like this can become expensive as wp_postmeta grows:
SELECT post_id
FROM wp_postmeta
WHERE meta_key = '_example_code'
AND meta_value LIKE '%sample-code%';
The substring condition on meta_value is the warning sign. WordPress core stores meta_value as LONGTEXT and does not index that column by default, so substring matching can examine many metadata rows after the meta_key filter.
Trace the caller before changing the database. The right fix may be changing the data model, narrowing the query, avoiding substring matching, or caching a stable result. Do not add an index blindly to a production table.
If repeated object lookups remain costly after the query and data model are sound, review the WordPress Redis object cache guide. Persistent object caching can reduce repeated database reads, but it does not repair a poor query pattern.
How to measure autoloaded options on WordPress 6.6 and later
Since WordPress 6.6, the database can use several autoload states. Core's wp_autoload_values_to_autoload() currently treats yes, on, auto-on, and auto as values that should load. The WordPress 6.6 Options API dev note (opens in a new tab) documents the change.
For an exact current total, query those values through WP-CLI. LENGTH() returns bytes, so the first command gives the stored byte total for autoloaded option values. The second sorts individual options from largest to smallest.
TABLE="$(wp db prefix)options"
wp db query "SELECT COALESCE(SUM(LENGTH(option_value)),0) AS autoload_bytes FROM ${TABLE} WHERE autoload IN ('yes','on','auto-on','auto');" --skip-column-names
wp db query "SELECT option_name, LENGTH(option_value) AS size_bytes, autoload FROM ${TABLE} WHERE autoload IN ('yes','on','auto-on','auto') ORDER BY size_bytes DESC;"
You can run the same check directly in MySQL. Replace wp_options if the site uses a different table prefix.
SELECT COALESCE(SUM(LENGTH(option_value)), 0) AS autoload_bytes
FROM wp_options
WHERE autoload IN ('yes', 'on', 'auto-on', 'auto');
SELECT option_name, LENGTH(option_value) AS size_bytes, autoload
FROM wp_options
WHERE autoload IN ('yes', 'on', 'auto-on', 'auto')
ORDER BY size_bytes DESC;
The total tells you the size of the autoload set. The sorted rows tell you which options deserve ownership checks. Do not delete an unfamiliar option because it is large. Find the plugin or theme that owns it, confirm whether it still needs the data, and change autoload behavior through supported code where possible.
For the underlying rules and cleanup decisions, use the guide to WordPress autoloaded options. You can also paste the WP-CLI results into the free autoloaded options analyser to organize the review.
How to identify admin-ajax and Heartbeat requests
A slow wp-admin screen may be fine on initial load but become sluggish while it stays open. In the browser Network panel, filter for admin-ajax.php, select a request, and inspect its request payload. The action field identifies the AJAX action that WordPress is dispatching.
Do not assume every admin-ajax.php request is Heartbeat. A Heartbeat request uses the Heartbeat AJAX path, while plugins can register their own actions on the same endpoint. Record the action name, duration, initiator, and response before changing anything.
If Heartbeat itself is creating avoidable load, adjust its interval through the documented heartbeat_settings filter (opens in a new tab). WordPress introduced that filter in version 3.6.0.
add_filter( 'heartbeat_settings', function ( $settings ) {
$settings['interval'] = 60;
return $settings;
} );
This example changes the interval rather than disabling Heartbeat globally. Core uses Heartbeat for post-lock refreshes and includes a Heartbeat autosave path. Test editing, autosave, and concurrent editing behavior after the change.
How to inspect Action Scheduler and WP-Cron
On WooCommerce, open WooCommerce > Status > Scheduled Actions. Inspect Pending actions whose scheduled date is already past, then inspect Failed actions and their logs. A queue that is not moving can make admin work compete with background processing or can indicate that cron execution is not reliable.
Current Action Scheduler WP-CLI provides the action list command. Past-due actions are pending actions whose scheduled date has passed. To list them, then list failed actions, run:
wp action-scheduler action list \
--status=pending \
--date='now' \
--date_compare='<=' \
--fields=id,hook,status,group,scheduled_date
wp action-scheduler action list \
--status=failed \
--fields=id,hook,status,group,scheduled_date
For a past-due row, inspect the hook and group before running or deleting anything. For a failed row, use the Scheduled Actions screen to read its log and identify the failing callback. Fix the cause before manually pushing a blocked queue.
WP-Cron normally gets opportunities to run during site requests. If you replace that traffic-driven trigger with the system scheduler, disable WordPress's page-load trigger in wp-config.php:
define( 'DISABLE_WP_CRON', true );
Then call wp-cron.php from the server scheduler. Replace the example host with the site's canonical HTTPS host.
* * * * * wget --delete-after https://example.com/wp-cron.php
The official WordPress system scheduler guide (opens in a new tab) documents DISABLE_WP_CRON and server-level scheduling. Confirm the cron user can execute the request successfully before relying on this setup.
How to spot remote HTTP calls that delay wp-admin
License validation, update checks, feeds, and remote service calls can run during an admin request. If they use the WordPress HTTP API, Query Monitor lists the destination, response code, component, timeout, call stack, and elapsed time.
Start with the calls that consume a visible share of the slow request. Trace each call to its component and caller, then determine why it runs on that screen. A remote request needed only for a settings page should not be moved blindly, but repeated synchronous checks often belong behind caching or a scheduled refresh.
Check failures as well as slow successes. DNS problems, TLS errors, blocked outbound traffic, or an unreachable vendor endpoint can hold a PHP worker while the request waits for its configured timeout.
How to separate memory pressure from PHP worker exhaustion
WordPress uses WP_MAX_MEMORY_LIMIT when it attempts to raise memory for administration work. Core documents the default as 256M, or the original PHP memory_limit when that value is higher.
This is the documented configuration form:
define( 'WP_MAX_MEMORY_LIMIT', '512M' );
Place it in wp-config.php before WordPress loads wp-settings.php. Treat the value above as a configuration example from the WordPress documentation, not as a target for every site.
A larger ceiling can prevent an out-of-memory failure, but it does not make slow SQL, remote requests, or background jobs faster. It can also let each PHP process consume more memory.
PHP workers are a separate constraint. If all available workers are busy with slow admin, AJAX, cron, or checkout requests, later requests wait for a worker. Check host or PHP-FPM metrics for active workers, queueing, and process saturation while reproducing the same slow screen.
How to verify that the fix worked
Change one cause at a time. Then repeat the same URL, user role, browser action, and measurement method you recorded at the start.
Record the new document request duration and the relevant Query Monitor totals. If you changed AJAX behavior, recheck the same admin-ajax.php action. If you changed cron handling, confirm the scheduled queue is advancing without creating new failures.
This discipline is the safest way to fix slow WordPress admin behavior without mistaking a random fast request for an improvement.
What to do next
Keep the before-and-after record with the site, then fix the largest confirmed source of delay first. If the cause spans SQL, background queues, object caching, and server capacity, use the WordPress performance optimization service for a structured audit and remediation plan.
Frequently asked questions
Why is WordPress admin slow while the website is fast?
The public site and wp-admin can take different request paths. Public pages may come from a page cache, while admin requests still run authenticated PHP, database queries, plugin callbacks, AJAX, and remote calls.
Can too many autoloaded options slow wp-admin?
Yes, because WordPress loads autoloaded options as part of normal bootstrap work. On WordPress 6.6 and later, include yes, on, auto-on, and auto when measuring the autoload set.
Should I disable the WordPress Heartbeat API to speed up admin?
Disabling Heartbeat everywhere can interfere with features that depend on it, including post locking and editor autosave behavior. Identify the actual admin-ajax.php action first, then adjust Heartbeat through heartbeat_settings only if the measurements justify it.
Can increasing the WordPress memory limit fix a slow dashboard?
A higher memory ceiling can prevent memory exhaustion when PHP is allowed to raise the limit. It does not fix slow queries, blocked HTTP calls, cron backlogs, or exhausted PHP workers, so measure those causes separately.
How this article was made: drafted with AI assistance from a researched brief, then checked against primary documentation before publishing.
Share this article
Enjoyed this? Get the next article by email.
Keep reading
Performance8 min read
How to find and fix slow WordPress database queries
WordPress slow database queries are usually found by measuring the actual SQL, its execution time, and how often it runs before changing indexes or code.
- WordPress
- Database
- MySQL
Performance6 min read
Why a cache purge can overload WordPress and how to stop it
A WordPress cache stampede happens when many requests lose the same cached data at once and all try to rebuild it. The fix is to limit rebuilds with a shared lock, keep stale data available during refresh, and spread expiries so misses do not arrive as one burst.
- WordPress
- Caching
- Performance
Performance8 min read
WordPress Redis object cache: setup and measurement
A WordPress Redis object cache keeps WordPress object-cache data available across requests, so PHP can reuse cached query results and other objects instead of rebuilding them each time.
- WordPress
- Redis
- Object Cache