Back to blog

Why Your WordPress Admin Is Slow (and Why That's a Different Problem)

A fast front end and a sluggish wp-admin is normal, not contradictory. Page caching can't touch the admin, so it exposes your real backend speed.

Why Your WordPress Admin Is Slow (and Why That's a Different Problem)

You run a speed test on your homepage and it scores well. Then you log in, open the post list, and wait four seconds for it to render. Someone suggests clearing your cache, which does nothing, because the cache was never involved in the first place.

A fast front end and a slow admin isn't a contradiction. It's the expected outcome when page caching is doing all the work on one side and none on the other. Your front-end score measures how well your cache is configured. Your admin speed measures how fast your site actually is.

That makes wp-admin a genuinely useful diagnostic. It's also fixable, though the levers are almost entirely different from the ones front-end optimisation guides talk about.

TL;DR

  • Page caching can't serve wp-admin, because every admin screen is personalised, nonce-protected, and specific to the logged-in user. Everything you see there is generated fresh.
  • The Heartbeat API polls the server on a documented interval range of 15 to 120 seconds while an admin screen is open, and each tick is an uncached admin-ajax.php request that boots all of WordPress.
  • admin-ajax.php is a single chokepoint. WordPress's documentation states "All WordPress AJAX requests must be sent to wp-admin/admin-ajax.php," and every one of those loads the full WordPress environment.
  • Update checks for core, plugins, and themes are scheduled twice daily via WP-Cron and hit api.wordpress.org. They're also triggered on specific admin screens, so a slow or unreachable API adds latency directly to admin page loads.
  • WP-Cron fires on page loads, not on a system timer. WordPress's handbook states plainly: "WP-Cron does not run constantly as the system cron does; it is only triggered on page load."
  • Autoloaded options are loaded on admin requests too, so wp_options bloat slows the admin exactly as much as the front end, and there's no cache hiding it.

Why the Cache Doesn't Help

Page caching works by storing generated HTML and serving it to subsequent visitors without running PHP. That's only safe when every visitor should see the same thing.

wp-admin fails that test on every screen. The post list depends on your role and capabilities. Nonces are user-and-action specific and expire. Notices are dismissed per user. Draft posts are visible to their author and hidden from others. Serving one administrator's cached admin screen to another user would be both broken and a security problem.

WordPress sets a wordpress_logged_in_[hash] cookie after login, which its documentation describes as indicating "when you're logged in, and who you are, for most interface use." Caching layers universally treat the presence of that cookie as a signal to bypass cache entirely. This is convention rather than something core mandates in a specific document, but it's implemented consistently across caching plugins and CDNs.

The consequence is worth stating clearly: your admin speed is your site's real speed with the makeup off. If wp-admin is slow, your front end would be slow too without its cache, which means your cache is carrying a problem rather than solving it.

The Heartbeat API

WordPress runs a periodic AJAX poll while you have an admin screen open. It powers autosave, the "someone else is editing this post" lock, and session expiry warnings.

WordPress's Plugin Handbook documents the interval as configurable within a range: "the client-side heartbeat code sets up an interval (called the 'tick') to run every 15-120 seconds."

Each tick is an admin-ajax.php request. Each one is uncacheable, boots the full WordPress environment, and runs whatever plugins have hooked into it. On a single admin session that's minor. With five editors each holding a post editor open all afternoon, it becomes a steady stream of uncached requests that never appears in your analytics.

To lengthen the interval:

php
function magicwp_heartbeat_settings( $settings ) {
    $settings['interval'] = 60; // Documented range is 15-120
    return $settings;
}
add_filter( 'heartbeat_settings', 'magicwp_heartbeat_settings' );

One caveat worth flagging. The specific per-screen defaults you'll see quoted in most articles, typically "15 seconds in the editor, 60 on the dashboard," aren't stated in WordPress's own handbook, which documents only the configurable range. An active Trac discussion has been revisiting both the defaults and the historical 15-second minimum. Treat the exact current default for any given screen as version-dependent, and check it on your own install rather than trusting a number from a blog post.

Be careful about disabling Heartbeat entirely. It's what powers autosave and post locking. Turn it off completely and you remove the protection against two editors overwriting each other's work, and the autosave that recovers a post after a browser crash. Lengthening the interval is nearly always the better trade than switching it off.

admin-ajax.php

WordPress's Plugin Handbook is unambiguous: "All WordPress AJAX requests must be sent to wp-admin/admin-ajax.php." Requests carry an action parameter that routes them to a hooked PHP callback.

The design advantage is real. Every AJAX handler automatically has the full WordPress environment available, so a plugin doesn't need to bootstrap anything itself.

The cost is the same thing. Every AJAX call, however trivial, loads all of WordPress: core, every active plugin, and the theme's functions file. A request that returns a two-word JSON response still pays the full bootstrap. And because they all share one endpoint, they queue against the same server resources.

The modern alternative is the REST API, and WordPress's own documentation frames it that way: "The WordPress REST API can also serve as a strong replacement for the admin-ajax API in core," noting that "AJAX calls can be greatly simplified by using the REST API."

You usually can't change how a third-party plugin does AJAX. What you can do is notice when one is doing it constantly. Open your browser's network tab on an admin screen, leave it a minute, and count the admin-ajax.php requests. If a plugin is polling every few seconds for something that doesn't need it, that's your answer, and it's often a plugin you can configure or replace.

Update Checks and api.wordpress.org

WordPress checks for updates to core, plugins, and themes by calling out to api.wordpress.org.

The mechanics, from WordPress's function reference:

  • wp_version_check() posts to https://api.wordpress.org/core/version-check/1.7/ and stores the result in the update_core site transient.
  • wp_update_plugins() posts your installed plugin list to https://api.wordpress.org/plugins/update-check/1.1/, storing results in the update_plugins transient, with a 12-hour default interval for non-cron checks.
  • wp_update_themes() follows the same pattern for the update_themes transient.
  • wp_schedule_update_checks() schedules all three on WP-Cron's twicedaily recurrence.

They're also triggered directly on certain admin screens, including the plugins page.

The practical implication: these are outbound HTTP requests made during an admin page load. If api.wordpress.org is slow to respond, or unreachable from your server, that latency lands directly on whoever happens to be loading that admin page. A site with 40 plugins is posting a substantial payload for that check.

This is a documented mechanism rather than a documented warning, so treat the causal claim as reasoning from how it works rather than something WordPress states outright. But it's a real and frequently observed cause of admin screens that hang for a few seconds and then load normally on refresh.

WP-Cron

This one surprises people who assume WordPress has a real scheduler. It doesn't.

From the Plugin Handbook: "WP-Cron works by checking, on every page load, a list of scheduled tasks to see what needs to be run." And explicitly: "WP-Cron does not run constantly as the system cron does; it is only triggered on page load."

Mechanically, _wp_cron() hooks into the shutdown action and calls spawn_cron(), which sends a non-blocking HTTP request to run due tasks. The non-blocking part matters, since it means cron doesn't hold up the response the visitor is waiting for.

Where it becomes a problem is volume. Every page load checks the queue. On a busy site that's a lot of checking. And if many tasks come due together, they all fire in the spawned request, which competes for the same server resources as everything else.

The documented consequence of the page-load trigger runs the other way too: "Scheduling errors could occur if you schedule a task for 2:00PM and no page loads occur until 5:00PM." A low-traffic site runs its cron late.

The standard fix is to disable the page-load trigger and use a real system cron:

php
define( 'DISABLE_WP_CRON', true );

Then schedule wp-cron.php externally, which WordPress's handbook documents as the approach for sites with tasks that must run on time. The handbook notes that leaving the default trigger running once you've moved to a real scheduler "will contribute to extra resource usage on your server."

The Post List on Large Sites

Sites with tens of thousands of posts often find the post list specifically slow.

Part of that is the status counts shown above the list. wp_count_posts() handles this, and WordPress's documentation describes it as "an efficient method of finding the amount of post's type a blog has," explicitly better than iterating get_posts(), which "has a lot of overhead."

It's cached, using wp_cache_get() and wp_cache_set() in the counts group. With a persistent object cache, it's cheap after the first computation. Without one, it recomputes on the relevant admin loads. The underlying query is a GROUP BY post_status aggregate, with additional subqueries when permission filtering for private posts applies.

Core has actively tuned this. A recent optimisation changed the private-post-counting path to use subqueries that "can leverage DB indexes for better performance."

The practical takeaway for large sites: a persistent object cache helps the admin disproportionately, because so much of what the admin does is uncacheable at the page level but very cacheable at the object level.

Autoloaded Options Hit the Admin Too

Worth stating explicitly because it's easy to miss. wp_load_alloptions() runs on admin requests exactly as it does on front-end requests. If your wp_options table has accumulated megabytes of autoloaded data, every admin page load pays that cost, and unlike the front end there's no page cache hiding it.

WordPress 6.6 added a Site Health check that warns when total autoloaded data exceeds 800,000 bytes, and stopped autoloading individual options over 150,000 bytes by default. Checking Tools → Site Health is a reasonable first move on any slow admin, and it takes ten seconds.

A Diagnostic Order

  1. Check Tools → Site Health. It surfaces autoloaded option bloat, a missing object cache, an outdated PHP version, and failing loopback requests, all in one place.
  2. Open the network tab on a slow admin screen and watch for repeated admin-ajax.php calls. A plugin polling aggressively is the single most common cause of a sluggish-feeling admin.
  3. Time a plugins-page load specifically. If it's dramatically slower than other admin screens, suspect the update check and outbound requests to api.wordpress.org.
  4. Install a profiler. Query Monitor shows queries, hooks, and HTTP API calls per request, with the responsible plugin attributed. It works in the admin, which is exactly where you need it.
  5. Check whether you have a persistent object cache. Its absence hurts the admin more than the front end, because the admin can't fall back on page caching.
  6. Lengthen the Heartbeat interval if you have multiple concurrent editors.
  7. Move WP-Cron to a real system cron on busy sites.
  8. Deactivate plugins one at a time on staging if you still can't attribute it. Crude, but it works when profiling doesn't produce an obvious culprit.

Important: Do plugin bisection on a staging copy, not on a live site with editors working. MagicWP's one-click staging makes a clone quick, and SSH with WP-CLI on every site lets you toggle plugins from the command line rather than through the admin you're trying to fix.

One Gap Worth Naming

There is no single authoritative WordPress document titled "how to speed up wp-admin." The core team publishes dev notes on specific improvements, like the 6.6 autoload work, and the Advanced Administration Handbook has general performance guidance, but admin performance as a topic doesn't have a consolidated official guide.

That means most advice in this area, including the sequencing above, is assembled from documented mechanics rather than quoted from an official recommendation. The mechanics are documented and verifiable. The prioritisation is judgement.

Frequently Asked Questions

Why is my WordPress admin slower than my website? Because page caching serves your front end but can't serve wp-admin. Every admin screen is personalised to the logged-in user and generated fresh. Your front-end score largely reflects your cache; your admin speed reflects your actual application performance.

Does a caching plugin speed up wp-admin? Not through page caching, no. Caching layers bypass the cache when the WordPress logged-in cookie is present, which is correct behaviour. What does help the admin is object caching, which caches database query results rather than whole pages.

What is the WordPress Heartbeat API and should I disable it? It's a periodic AJAX poll that powers autosave, post locking, and session expiry warnings, running on a documented 15 to 120 second interval. Lengthen it rather than disabling it, since turning it off removes autosave and the protection against two editors overwriting each other.

Why is admin-ajax.php slow? Every AJAX request goes through that single endpoint and loads the entire WordPress environment, including all active plugins, even for a trivial response. A plugin polling it frequently generates a steady stream of uncached full-bootstrap requests.

Does WP-Cron slow down my site? It checks the schedule on every page load, and if many tasks come due at once they fire together and compete for server resources. On busy sites, disabling the page-load trigger with DISABLE_WP_CRON and running wp-cron.php from a real system cron is the standard fix.

Why does the Plugins page take longer to load than other admin screens? It triggers an update check, which posts your installed plugin list to api.wordpress.org and waits for a response. If that API is slow or unreachable from your server, that wait lands on your page load.

Does the number of posts affect admin speed? It can, particularly the post list, where status counts run a GROUP BY aggregate. That result is cached in the counts object cache group, so sites with a persistent object cache see much less impact than sites without one.

How do I find which plugin is slowing down my admin? Query Monitor is the fastest route, since it attributes queries, hooks, and HTTP requests to the responsible plugin on any admin screen. Failing that, deactivate plugins one at a time on a staging copy.

Conclusion

A slow WordPress admin isn't a separate problem from site performance, it's the same problem without a cache in front of it. That's what makes it useful: it tells you what your site actually costs to run, rather than how well your cache is configured.

The fixes are mostly unglamorous. Check Site Health for autoloaded option bloat. Watch the network tab for a plugin polling admin-ajax.php every few seconds. Add a persistent object cache, which the admin benefits from more than the front end does. Lengthen Heartbeat if you have a team. Move cron off page loads on a busy site.

And when the admin gets faster, notice that your front end got faster too, in the part your cache was hiding.

A
Alex
MagicWP
Writing about WordPress, performance, and the infrastructure that makes sites fast.

Get the best of MagicWP in your inbox.

Monthly engineering notes, product updates, and WordPress performance tips. No spam, unsubscribe anytime.

Join 12,000+ builders. We send one email a month.