Open your browser’s network panel on a WordPress admin page and watch. Every 15 seconds, a POST request fires to /wp-admin/admin-ajax.php. That is the Heartbeat API: a polling mechanism built into WordPress core that keeps the editor autosave working, detects logged-out sessions, and powers features like post locking. On most admin pages, it is doing nothing useful while burning server resources with every single request it fires.
On a busy site with multiple logged-in editors, or on shared hosting with tight PHP worker limits, Heartbeat can cause real slowdowns. The fix is not a plugin – it is two or three lines of PHP in a mu-plugin. This guide covers exactly what Heartbeat does, where to disable it, and how to tune frequency using the heartbeat_settings filter and AUTOSAVE_INTERVAL constant. For broader site speed optimization strategies, visit our performance hub.
Heartbeat is a JavaScript polling loop. It sends a POST request to admin-ajax.php on a fixed interval (15 seconds by default), passes data registered by any plugin or core feature via the heartbeat_send filter, receives the server response, and dispatches events via heartbeat_received and related hooks. The server-side handler is in wp-admin/includes/ajax-actions.php and runs a full WordPress bootstrap for each request.
The polling interval is set in JavaScript when the script is enqueued. You can see the configuration object in the page source as var heartbeatSettings = { "interval": 15, "nonce": "..." };. The heartbeat_settings PHP filter modifies this object before it is output, giving you server-side control over the client-side polling behavior. The filter receives an array and must return the modified array – it is not a side-effect filter.
Features That Depend on Heartbeat
| Feature | What It Does | Where Active |
|---|---|---|
| Post autosave | Saves draft content to the database | Post editor only |
| Post locking | Prevents two users editing the same post | Post editor only |
| Login expiry warning | Prompts before session expires | All admin pages |
| Dashboard widgets | Some widgets use Heartbeat for live data | Dashboard only |
Notice that post autosave and post locking only matter on the post editor. The login expiry warning is useful on all admin pages. On the frontend – your site visitors never see the admin – there is absolutely no reason for Heartbeat to run at all. Yet by default it fires on every WordPress page load when the user is logged in, including your homepage, single post pages, and product pages on a WooCommerce store.
Each Heartbeat request bootstraps WordPress fully: loads wp-config.php, connects to the database, initializes all active plugins, and runs the request handler. On shared hosting with 25ms average PHP execution time per request and a site with 10 logged-in admin users, that is 40 requests per minute adding up to roughly 4 full PHP bootstrap cycles per second. Multiply that by the number of plugins you have active, and you understand why Heartbeat shows up prominently in server load graphs.
The impact is especially pronounced on pages where Heartbeat serves no purpose. The Media Library, Settings pages, and plugin list pages all fire Heartbeat at full speed by default. Disabling it there costs you nothing and reduces your server load immediately. For sites on shared hosting plans that throttle PHP workers, this can be the difference between a responsive admin and one that times out during peak edit sessions.
A practical example: a WordPress agency managing 50 client sites on a shared server, each with 2-3 editors. If all clients are working simultaneously and Heartbeat fires every 15 seconds per editor, that is potentially 10 requests per minute per site – 500 admin-ajax.php requests per minute across the server. Tuning each site to 60-second intervals and disabling frontend Heartbeat drops that to under 100 requests per minute for the same workload. That is an 80% reduction in Heartbeat-related PHP load with no change in editor functionality for any of those users.
The best approach is surgical: keep Heartbeat running on the post editor where it powers autosave and post locking, and disable it everywhere else. Drop this in wp-content/mu-plugins/heartbeat-control.php:
The key detail: this runs on the init hook with priority 1 to deregister the script before any plugin can enqueue it. Deregistering a script in WordPress is permanent for that page load – once wp_deregister_script() runs, nothing can re-enqueue it. The heartbeat_settings filter then slows the remaining editor requests from 15 to 60 seconds, cutting server load on the editor by 75%.
Why run this on init rather than wp_enqueue_scripts? The wp_enqueue_scripts hook does not fire in the admin. For admin pages, you would need admin_enqueue_scripts. Running on init before any script queuing happens is simpler and covers both frontend and admin with a single hook registration. The early priority (1) ensures your deregistration runs before WordPress core has a chance to enqueue the script through its own boot sequence.
For more granular control, use the admin_enqueue_scripts hook which provides the current admin page hook name. This lets you make per-page decisions rather than a blanket disable:
This approach is useful if you have plugins that use Heartbeat for specific admin pages – like a plugin that shows live order notifications on the WooCommerce orders page. You can keep Heartbeat active there at a reasonable interval while disabling it everywhere it serves no purpose. The $hook parameter matches the page hook returned by add_menu_page() and add_submenu_page() calls, so you can target any third-party plugin’s admin page specifically.
Autosave and Heartbeat are related but separable. Autosave uses Heartbeat to send the save request, but the autosave interval is a separate setting. Reducing Heartbeat frequency from 15 to 60 seconds also reduces autosave frequency. If you want to keep more frequent autosaves but slow Heartbeat for post locking checks, you can configure them independently:
The constant approach in wp-config.php is the simplest – it sets the autosave interval globally and does not require a plugin or hook. The filter approach is more flexible when you want different intervals on different post types, or when you need to conditionally disable autosave on specific screens. Note that the autosave interval cannot be lower than the Heartbeat interval – setting autosave to 30 seconds with Heartbeat at 60 seconds means autosave fires at most every 60 seconds in practice.
Disabling Heartbeat on all admin pages (not just frontend and non-editor pages) breaks several things you may not realize depend on it:
- Post locking stops working: Two editors can silently overwrite each other without the lock warning. This matters on multi-author sites.
- Autosave stops working: Draft content will not be saved automatically. A browser crash or session timeout loses all unsaved work.
- Session expiry warnings stop: Editors will be silently logged out and lose unsaved work when their session expires.
- Plugin features break: Some plugins – particularly WooCommerce dashboard widgets, monitoring plugins, and live chat plugins – use Heartbeat for real-time data. Check your plugins before disabling globally.
- Scheduled content warnings disappear: If you use plugins that warn about conflicting publish schedules, those alerts often run through Heartbeat.
The safe approach is always to disable on frontend and specific non-essential admin pages, and reduce frequency (never fully disable) on the editor. A 60-second interval on the editor keeps all important features working while cutting server load by 75% compared to the default 15-second interval. This is the configuration the Heartbeat Control plugin by WP Rocket defaults to for a reason – it is the right balance of performance and functionality. If Heartbeat issues are just one symptom of broader admin slowdowns, our WordPress troubleshooting guides cover additional fixes.
Heartbeat is still firing after I added the mu-plugin
Check whether another plugin is re-enqueuing the Heartbeat script after your mu-plugin deregisters it. This should not be possible since wp_deregister_script() is permanent, but some poorly coded plugins call wp_register_script() with the heartbeat handle to override it. If you see Heartbeat still firing, add error_log('heartbeat check: ' . (wp_script_is('heartbeat', 'registered') ? 'registered' : 'not registered')); to a late hook like wp_footer to confirm your deregistration stuck.
The heartbeat_settings filter is not changing the interval
The filter only fires when the Heartbeat script is registered and the settings object is being output. If you deregister the script on a page, the filter has nothing to modify on that page. To confirm the filter is running, add a log statement inside your filter callback. Also verify you are not running the filter on a page where you have already deregistered the script – the filter is redundant (but harmless) in that case.
Autosave stopped working after I changed the interval
If you set the AUTOSAVE_INTERVAL constant to a very high value (like 3600 seconds), autosave will still run – just infrequently. If autosave stopped entirely, check whether you deregistered the Heartbeat script on the editor page by mistake. The $pagenow === 'post.php' check in the example code keeps Heartbeat active on the editor. Verify your logic covers both post.php and post-new.php for new post creation.
The behavior differs slightly between editors. In the classic editor, Heartbeat handles both autosave and post locking. In the block editor (Gutenberg), autosave has a partially independent implementation using the REST API – block editor autosave can fire via REST POST requests to the posts endpoint in addition to Heartbeat. This means reducing Heartbeat frequency on the block editor has less impact on autosave frequency than it does in the classic editor.
For block editor sites, the most effective approach is still to disable Heartbeat on non-editor pages and reduce frequency on the editor – but the autosave frequency reduction will be less dramatic than on classic editor sites. The post locking check still goes through Heartbeat in both editors, so the server load reduction from frequency tuning still applies regardless of which editor your site uses.
Before and after tuning Heartbeat, measure the actual impact on your server. Query Monitor shows Heartbeat-related AJAX requests in its AJAX panel, including the execution time and database queries each request triggers. On a typical WordPress site with 30 active plugins, a single Heartbeat request can execute 40-80 database queries and take 100-300ms of PHP execution time. Multiply that by 4 requests per minute at the default 15-second interval, and you get 160-320 queries per minute and 400-1200ms of PHP time consumed per minute, per logged-in user.
Server-side monitoring provides a broader view. Check your Apache or Nginx access logs for requests to admin-ajax.php with the action=heartbeat parameter. On Apache, a quick grep through the access log counting heartbeat requests per hour shows you the baseline before tuning and the reduction after. Most hosting control panels (cPanel, Plesk, Cloudways) also show PHP worker utilization graphs, look for a sustained baseline of AJAX requests that drops noticeably after you implement Heartbeat controls.
For ongoing monitoring, the free WP Crontrol plugin provides visibility into Heartbeat event scheduling, and New Relic or the Blackfire profiler can trace individual Heartbeat requests to identify which plugins are adding the most server-side processing to each heartbeat cycle. Some plugins hook into heartbeat_received and heartbeat_send to piggyback heavy operations onto the Heartbeat cycle, these plugins are often the real cause of slow admin pages, not the Heartbeat mechanism itself. Identifying and optimizing these piggyback operations sometimes provides more benefit than reducing the Heartbeat frequency.
Starting with WordPress 5.0 and the block editor, some functionality that previously relied exclusively on Heartbeat now has REST API alternatives. The block editor’s autosave, for example, uses REST API endpoints (/wp/v2/posts/{id}/autosaves) rather than the Heartbeat AJAX handler. This means the block editor makes two types of background requests: Heartbeat AJAX calls for post locking and session checks, and REST API calls for autosave content.
Understanding this distinction matters for server optimization. If you disable Heartbeat on the block editor entirely, you lose post locking and session expiry warnings, but autosave continues working through the REST API. Some developers prefer this trade-off on single-author sites where post locking is irrelevant. However, this is not recommended for multi-author sites where two editors could unknowingly work on the same post and overwrite each other’s changes without the Heartbeat-powered lock warning.
The REST API autosave requests are lighter than Heartbeat requests because they only send the post content diff, not the full Heartbeat payload. They also fire on a content-change basis rather than a fixed timer, the block editor only sends an autosave when the content has actually changed, unlike Heartbeat which fires regardless of whether anything happened. This means the REST API autosave is already more efficient than the classic editor’s Heartbeat-based autosave, and the argument for aggressively tuning Heartbeat frequency is even stronger on block editor sites.
Different site configurations need different Heartbeat settings. Here is a quick reference for the most common scenarios developers encounter when tuning WordPress admin performance:
- Single-author blog, no plugin using Heartbeat: Disable entirely on non-editor pages, set editor interval to 120 seconds. Autosave every 2 minutes is sufficient for most writers.
- Multi-author news site: Disable on frontend only. Keep editor Heartbeat at default 15 seconds – post locking matters when multiple editors work simultaneously on breaking news.
- WooCommerce store, shop managers checking orders: Disable on frontend. Keep at 60 seconds for admin. Check if your WooCommerce dashboard widgets need Heartbeat before disabling on the Dashboard page.
- Agency site, developers only in admin: Disable on frontend and all non-editor pages. Set editor to 60 seconds. Developers can handle losing an occasional autosave better than clients can.
- Client-managed membership site: Keep Heartbeat active everywhere but at 60 seconds. Non-technical clients need the session expiry warning and the safety net of frequent autosaves.
- High-traffic site on managed hosting: Coordinate with your host. Many managed WordPress hosts (WP Engine, Kinsta) already tune Heartbeat at the infrastructure level. Check before adding your own tuning to avoid conflicts.
The minimum you should do on any WordPress site is disable Heartbeat on the frontend. This requires a single wp_deregister_script() call, takes 30 seconds to implement, and has zero risk of breaking anything for logged-in users on the admin side. After that, evaluate your specific editor usage patterns and tune the interval accordingly.
On WordPress multisite installations, Heartbeat runs independently on each sub-site. If your network has 20 sub-sites and each has 2 logged-in editors, that is 40 Heartbeat connections firing against the shared database server. The impact compounds because multisite shares the same MySQL instance across all sites, Heartbeat requests from one sub-site compete for database connections with requests from every other sub-site on the network.
The mu-plugin approach works well for multisite because mu-plugins are loaded network-wide automatically. Place your Heartbeat control mu-plugin in the wp-content/mu-plugins/ directory and it applies to every sub-site without per-site configuration. If individual sub-sites need different settings, for example, the main editorial site needs faster Heartbeat than a documentation sub-site, you can use get_current_blog_id() or get_site_url() inside your mu-plugin to apply different intervals per site.
Network administrators should monitor Heartbeat impact at the server level rather than per-site. Watch your MySQL slow query log for admin-ajax.php related queries, and check your PHP-FPM pool status to see how many workers are occupied by Heartbeat requests at any given moment. On networks where Heartbeat consumes more than 20 percent of available PHP workers, aggressive tuning, 120-second intervals on the editor, completely disabled elsewhere, is justified even if it means slightly less frequent autosaves for editors.
Several popular WordPress plugins add their own data to the Heartbeat cycle, increasing the server cost of each request. WooCommerce adds order notification checks on admin pages. BuddyPress and BuddyBoss add real-time notification polling. Live chat plugins like Crisp and Tawk.to sometimes piggyback on Heartbeat for agent notifications. Analytics dashboard plugins may use Heartbeat to update real-time visitor counts.
Each plugin that hooks into heartbeat_send or heartbeat_received adds database queries and processing time to every single Heartbeat request. A site with 5 plugins hooking into Heartbeat might see each request take 500ms instead of the typical 100-200ms for a clean WordPress install. Before tuning Heartbeat frequency, audit which plugins are adding to the Heartbeat payload. Use Query Monitor to inspect the AJAX response and identify which plugins are adding data.
In some cases, the better optimization is not reducing Heartbeat frequency but removing unnecessary plugins from the Heartbeat cycle. If a WooCommerce notification widget on the dashboard is adding 200ms to every Heartbeat request, and you do not need real-time order notifications on the dashboard, unhook that specific feature rather than slowing down Heartbeat globally. Use remove_filter() or remove_action() to detach specific plugin callbacks from the Heartbeat hooks while leaving the core Heartbeat functionality intact.
| Plugin Category | Typical Heartbeat Impact | Recommendation |
|---|---|---|
| WooCommerce (order notifications) | 50-150ms per request | Disable on non-WooCommerce admin pages |
| BuddyPress/BuddyBoss (notifications) | 100-300ms per request | Use REST API polling instead |
| Live chat (agent alerts) | 50-100ms per request | Use plugin’s own WebSocket connection |
| Analytics dashboards | 100-200ms per request | Disable real-time updates, use cached data |
| Security plugins (login monitoring) | 30-80ms per request | Usually lightweight enough to keep |
The recommended configuration for most WordPress sites: disable Heartbeat on the frontend entirely, disable it on admin pages that do not need it (media, settings, plugins, themes), and set the interval to 60 seconds on the post editor. Add the AUTOSAVE_INTERVAL constant to wp-config.php if you want more control over draft saving frequency independent of Heartbeat.
This is the same configuration you get from plugins like Heartbeat Control by WP Rocket – but without the plugin overhead, without an additional dependency to maintain, and with full control over the logic. The mu-plugin approach is also more reliable: mu-plugins load before regular plugins, so your Heartbeat settings apply before any plugin can override them. You also avoid the common problem of performance plugins conflicting with each other, when two plugins both try to manage Heartbeat, unpredictable behavior results. A single mu-plugin with clear, simple logic eliminates this conflict entirely and gives you one authoritative place to configure Heartbeat behavior across your entire site.
For more server-level WordPress performance tuning, see the guide on removing render-blocking CSS and JavaScript for front-end page load improvements. For security-focused server hardening that also reduces unnecessary background request handling, see our complete guide on protecting WordPress from malware and crypto miners which covers additional wp-config.php constants and server-level protections.
AJAX Performance Heartbeat API Performance Optimization Server Load WordPress Admin Optimization
Last modified: March 26, 2026