
admin-ajax.php Is Slow—How to Find the WordPress Request Causing It
A WordPress dashboard can feel instant until one action—saving a post, opening a plugin screen, changing a filter or simply leaving the editor open—triggers a request to admin-ajax.php that runs for several seconds. The browser shows the endpoint, but that file is rarely the real culprit. It is the shared doorway through which many WordPress core, theme and plugin actions pass.
When I diagnose this problem, I do not block admin-ajax.php or disable every plugin. I capture one slow request, identify its action and initiator, reproduce it safely, then trace the server work behind that exact action. That keeps the investigation narrow and avoids breaking autosave, post locking, ecommerce fragments, form validation or other features that legitimately depend on AJAX.
Why admin-ajax.php appears in so many slow requests
WordPress’s official AJAX documentation explains that AJAX requests are sent to wp-admin/admin-ajax.php and include an action parameter. WordPress uses that action value to route the request to the relevant callback. The endpoint therefore tells you where the request entered WordPress—not which plugin, database query or remote service made it slow.
The same URL can handle a lightweight setting check in a fraction of a second and a heavy import, search, report or API sync in many seconds. Treating every admin-ajax.php request as one problem is like blaming a reception desk because one visitor took an hour-long meeting.
First decide whether the problem is frequency, duration or both
One request takes a long time: investigate the callback, database queries, PHP work and external HTTP calls behind that action.
Many fast requests arrive continuously: identify the script that schedules them and whether the frequency is necessary.
Many slow requests overlap: check browser tabs, Heartbeat, background plugin tasks, database contention and PHP worker capacity.
The request waits before server processing begins: investigate hosting queues, worker saturation, CDN or security inspection rather than only WordPress code.
The request returns quickly but the interface still freezes: examine front-end JavaScript work after the response.
The useful unit of diagnosis is not admin-ajax.php. It is one request with a timestamp, action, initiator, payload, duration and response.
Step 1: Capture the slow request in the browser
Open the affected WordPress screen in a private window or a clean browser profile. In Developer Tools, select Network, filter for admin-ajax.php and reproduce the slow action once. Preserve the log if navigation clears it.
Record the request start time and total duration.
Open the Payload or Form Data panel and find the action value.
Check the Initiator or call stack to identify the JavaScript file that sent it.
Note the HTTP status, response size and response body.
Use the Timing panel to separate queueing, connection, waiting for server response and download time.
Confirm whether the request repeats on an interval or only follows a user action.
Do not publish nonces, cookies, user IDs or customer data from the payload. You need the action name and timing pattern, not sensitive authentication material.
Step 2: Recognise a Heartbeat request before changing it
WordPress includes a Heartbeat API for near-real-time communication. According to the official Heartbeat API guide, its client tick can run every 15 to 120 seconds and an admin-ajax handler processes the data. Heartbeat supports useful features such as autosave, post locking and session checks.
If the payload action is heartbeat, inspect the data sent with it and the screen where it occurs. A plugin can attach expensive work to Heartbeat, so the correct fix may be removing that plugin callback or reducing unnecessary data—not disabling Heartbeat across the entire site.
A healthy recurring Heartbeat request is not automatically evidence of a problem. Compare its server time, frequency and concurrent editor sessions. Several open admin tabs can each create their own pattern, making a normal feature look like an attack in a raw access log.
Step 3: Map the action to the responsible code
Search the active theme, must-use plugins and plugins directory for the action name. WordPress AJAX callbacks are registered through hooks whose names usually include wp_ajax_ followed by the action for authenticated users, or wp_ajax_nopriv_ for logged-out visitors.
add_action( 'wp_ajax_example_action', 'example_callback' );add_action( 'wp_ajax_nopriv_example_action', 'example_public_callback' );Finding the hook registration gives you a candidate owner. It does not yet prove that every slow millisecond belongs to that plugin. The callback may call WordPress APIs, run a database query, generate files, invoke another plugin hook or wait for a remote service.
Step 4: Reproduce it on staging with controlled tests
Take a current backup and reproduce the issue on staging. Webcurry’s plugin slowdown diagnostic explains how to isolate extensions without randomly disabling everything on production.
Run the same action with all normal conditions unchanged, then disable only the suspected plugin or its specific feature. If the request disappears, confirm whether the functionality was expected. If it remains, inspect dependencies, must-use plugins and theme code. Always retest the same URL, account role, screen and payload.
Step 5: Measure server work behind the request
Correlate the browser timestamp with the web-server access log, PHP error log, application performance monitoring trace and database slow-query log. The most useful evidence tells you whether time was spent before PHP started, during WordPress bootstrap, inside the AJAX callback, waiting for MySQL or waiting for an external HTTP request.
Long database time can come from missing indexes, unbounded searches, autoloaded options, lock contention or repeated queries inside a loop.
Long external-call time can come from licensing servers, CRMs, payment providers, analytics services or blocked outbound requests.
High PHP time can come from image processing, report generation, imports, exports or expensive plugin filters.
Queueing before PHP can indicate too few PHP workers, exhausted hosting resources or overlapping background jobs.
A large response can waste time after the server has already done the expensive work.
If the whole dashboard is slow rather than one AJAX action, use Webcurry’s WordPress admin troubleshooting guide to check object caching, autoloaded data, loopback requests and hosting constraints.
Step 6: Enable logging without exposing errors to visitors
WordPress documents WP_DEBUG and WP_DEBUG_LOG for recording errors, notices and warnings. Its official debugging guide recommends using a staging environment or backup before changes and shows how to log errors while keeping display disabled.
define( 'WP_DEBUG', true );define( 'WP_DEBUG_LOG', true );define( 'WP_DEBUG_DISPLAY', false );@ini_set( 'display_errors', 0 );Logging does not measure every performance bottleneck, but it can expose repeated warnings, failed API calls, deprecated functions or database errors occurring during the slow request. Remove temporary diagnostic settings after the investigation and protect log files from public access.
Step 7: Check database and cache symptoms
An AJAX callback often bypasses full-page caching because it is user-specific or changes state. That means the request can reveal database or object-cache problems that normal front-end page tests hide. Inspect repeated option reads, transients, session rows, post-meta searches and queries that scan large tables.
Do not flush every cache repeatedly. A cache flush can temporarily increase load and destroy the evidence you need. If WordPress cannot connect reliably to the database, handle that separately using the database-connection recovery workflow.
Step 8: Keep WP-Cron separate from admin-ajax.php
WP-Cron is related to background load but normally runs through its own scheduling path, not admin-ajax.php. WordPress states that WP-Cron checks scheduled tasks on page loads, and duplicate scheduling can create thousands of recurring events if a plugin fails to test whether an event already exists. Review the official WP-Cron documentation and scheduled-event guidance before changing it.
If slow AJAX requests coincide with heavy cron events, investigate shared database locks, CPU, PHP workers and third-party APIs. Do not label the AJAX endpoint as the cause merely because both activities occur at the same time.
Common fixes that can break the site
Blocking /wp-admin/admin-ajax.php in a firewall or robots.txt file.
Disabling Heartbeat globally without checking autosave and post-locking requirements.
Increasing PHP memory without identifying which action consumes it.
Installing another optimisation plugin before measuring the existing callback.
Disabling every plugin on production and losing the original reproduction conditions.
Assuming a 200 status means the request was healthy; a successful response can still take ten seconds.
Blaming the database without reviewing the AJAX action and initiator.
Leaving debug logging enabled indefinitely or exposing debug.log publicly.
A practical decision tree
If the action is heartbeat, inspect attached data, screen context, interval and open tabs.
If the action belongs to a plugin, reproduce on staging and test that feature in isolation.
If server wait time dominates, profile PHP, MySQL and outbound HTTP calls.
If queueing dominates, check PHP workers, CPU, memory and concurrent background work.
If the request is fast but repeated excessively, fix the scheduling JavaScript or event binding.
If the response is fast but the screen remains slow, profile the client-side callback and DOM work.
If no action parameter exists, confirm that you are looking at a genuine WordPress AJAX request and not a rewrite, security challenge or cached error.
How to verify the fix
Repeat the original test with the same account, page, action and browser conditions. Compare the before-and-after waterfall, server trace and database time. Then test the feature that depended on the request: saving, locking, search, filtering, forms, checkout or background progress.
Also check real user behaviour. A good synthetic page-speed score cannot prove that logged-in workflows are healthy; Webcurry’s guide to fast lab scores but slow real visits explains why the testing context matters.
Final takeaway
admin-ajax.php is a dispatcher, not a diagnosis. Capture the action, initiator, payload pattern and timing; map the action to its callback; trace PHP, database and remote work; and verify the exact workflow after the change. That evidence-first process fixes the request that is actually slow while preserving the WordPress features that rely on AJAX.
Comments