A slow WordPress admin is almost always one request, not the whole dashboard. Open the browser network tab, click once inside wp-admin, and sort by time: either the HTML document is slow, or admin-ajax.php is slow, or load-scripts.php and load-styles.php are slow.
Each of those three points at a different cause, and the twelve-item checklists you have already read do not tell you which one you have.
We read the guides currently ranking for this phrase before writing. Four of them recommend Query Monitor, raising the memory limit, throttling the heartbeat and cleaning the database, in roughly that order.
None of the four tells you how to find out which request is slow, none mentions autoloaded options, and all of them have you deactivating plugins on the live site.
Step one: which of the three requests is slow
Open DevTools, go to the network tab, tick "Disable cache", and load /wp-admin/edit.php. Then read three numbers.
The document request is the HTML page itself. If that is the slow one, the time is PHP and MySQL work: plugin code on admin hooks, a bloated options table, or a genuinely overloaded server.
admin-ajax.php requests arrive after the page has painted. If the page appears quickly and then the interface feels sticky, this is your problem, and it is usually the heartbeat plus whatever a plugin attaches to it.
load-scripts.php and load-styles.php are the concatenators core uses when SCRIPT_DEBUG is off. If those dominate, you are looking at a transfer and compression problem, not a database one, and no amount of database cleanup will help.
This matters because the standard advice is mutually exclusive in practice. Raising memory_limit does nothing for a slow admin-ajax.php, and throttling the heartbeat does nothing for a slow document.
Autoloaded options: the number nobody checks
On every admin request, core runs wp_load_alloptions() and pulls every option row marked for autoload into memory in one query. Here is the part the checklists skip: a site can carry hundreds of kilobytes of autoloaded junk and still pass Site Health.
WordPress 6.6 added the autoloaded options test, and its warning threshold is 800,000 bytes, filterable through site_status_autoloaded_options_size_limit (verified in wp-admin/includes/class-wp-site-health.php, wordpress-develop trunk). Below 800 KB Site Health prints "which is acceptable".
A 600 KB blob being unserialized on every single admin click is not acceptable, but nothing in the dashboard will ever tell you about it.
Measure it directly. WP-CLI documents this exact call:
wp option list --autoload=on --format=total_bytes
The documented example output on a stock install is 33198, so about 33 KB. If yours comes back in the hundreds of thousands, find the culprits:
wp option list --autoload=on --fields=option_name,size_bytes | sort -n -k 2 | tail
In our experience the top of that list is almost never core. It is an abandoned plugin's cached API response, a licence check, or a log table that someone wrote into wp_options instead of its own table.
Since 6.6 core defends itself a little: add_option() and update_option() route anything over 150,000 bytes to auto-off, a threshold filterable as wp_max_autoloaded_option_size (wp-includes/option.php). That only applies to writes made after 6.6, so a row a plugin autoloaded in 2021 stays autoloaded forever.
There is also a trap worth knowing before you go editing the autoload column by hand. If the autoload query returns no rows at all, wp_load_alloptions() falls back to SELECT option_name, option_value FROM $wpdb->options with no WHERE clause, which loads the entire options table.
Set every row to off and you get the worst possible outcome rather than the fastest one.
The heartbeat is 60 seconds, and 10 in the editor
The repeated claim is that the heartbeat fires every 15 seconds. It does not.
In heartbeat.js, mainInterval is 60 seconds, and the Page Visibility API drops it to 120 once the tab is hidden. The block editor raises the rate itself with wp.heartbeat.interval( 10 ) (wp-admin/edit-form-blocks.php), so inside the editor it is 10 seconds.
The 15-second figure everyone repeats belongs to autosave, which is a separate local diff check in autosave.js, not a server request. Both facts are verifiable in wordpress-develop trunk, and the difference matters: if you are throttling the heartbeat to fix a slow dashboard, you are tuning a request that fires once a minute.
What makes the heartbeat expensive is rarely core. Each tick is a full WordPress load plus whatever plugins hooked onto heartbeat_received.
Watch admin-ajax.php in the network tab for two minutes and read the response bodies: if one of them is returning a large payload of order counts or notification data, you have found your plugin without installing anything.
Bisect plugins on a copy, not on the live site
Every guide reaches the same step eventually: deactivate the plugins, then reactivate them one at a time. On a live site that step logs your customers out of their sessions, empties carts, breaks scheduled jobs and, with a security plugin, can lock you out of the login page you just deactivated it from.
This is the part we can speak to, because it is what a WordPress sandbox exists for. Clone production into a disposable copy with Cloner, which reads your database and files to build the archive and does not modify anything on the live site, then do the destructive half of the diagnosis there.
Bisect rather than stepping through one plugin at a time. Deactivate half, measure, then halve the slow half again: twenty plugins takes about five rounds instead of twenty.
Over SSH, where wp is preinstalled, each round is two commands:
wp plugin deactivate plugin-a plugin-b plugin-c
time curl -s -o /dev/null -b cookies.txt https://your-sandbox.sandywp.dev/wp-admin/edit.php
Keep a saved cookie jar from a logged-in session, or you will be timing the login redirect rather than the dashboard. When the measurement is confusing, turn on debug mode and watch debug.log while the slow request runs.
When you want real numbers, profile the load
Query Monitor is in every guide and it is a good tool, but it shows you one request in a browser. For repeatable numbers, wp profile breaks the load into stages:
wp package install wp-cli/profile-command
wp profile stage --url=https://your-sandbox.sandywp.dev/wp-admin/
wp profile hook --all --url=https://your-sandbox.sandywp.dev/wp-admin/
It is a separate package, not a bundled command, and profile hook is the one that names the offender: it reports time and query count per hook, so an admin_init callback costing 900 ms shows up as a line of output rather than a hunch.
Install it on the sandbox. It is a package install plus a profiling run against a live dashboard, which is exactly the kind of thing worth keeping off production.
Rule the boring causes out while you are there
Two of the standard fixes are worth testing properly rather than applying blind, and a copy lets you test both by changing the thing and changing it back.
The memory limit. SandyWP sandboxes ship with memory_limit at 256 MB and let you set it between 64 and 512 MB, so you can prove the point in both directions: drop the copy to 64 MB and see whether the symptom appears, then raise it and see whether it goes. If neither change moves the number, memory was never the problem, whatever the checklist said.
The PHP version. Switching versions on a sandbox leaves its files and database alone, so comparing 8.2 against 8.5 on the same install takes a restart rather than a migration. If you are planning that upgrade anyway, upgrade testing is the same job.
What a sandbox cannot tell you
If the document request is slow and no plugin combination changes it, stop looking in WordPress.
A sandbox runs on different hardware from your host, with its own CPU share, its own MySQL and no object cache tier. It cannot reproduce a noisy neighbour on shared hosting, a connection-limited managed MySQL instance, a slow network path to your office, or contention from real traffic while you are clicking around in wp-admin.
So a copy that is fast proves the code is fine, not that your server is fine. At that point the honest answer is the one hosting companies give for their own reasons and which is sometimes simply true: the server is the limit, and the fix is a bigger server or a different host.
Our only quarrel with that advice is the order, because it belongs last, after you have measured.
What a sandbox does settle is the question you cannot safely ask on production: which plugin, which option, which hook. That takes about five rounds of bisecting and nothing of yours breaks while you do it.
