Type something to search...
How to Control the WordPress Heartbeat API to Reduce Server Load?

How to Control the WordPress Heartbeat API to Reduce Server Load?

Your hosting dashboard shows CPU usage spiking during office hours, even though traffic is flat. The server access log is full of POST requests to /wp-admin/admin-ajax.php, every few seconds, from the same handful of IP addresses: your own editors. Each request boots WordPress, loads every active plugin, and runs PHP that cannot be cached. On a small shared hosting plan, a few open admin tabs are enough to hit resource limits.

The source is usually the WordPress Heartbeat API, a background polling system that keeps the dashboard in sync with the server. Heartbeat is useful, and some of what it does protects your content. It also runs more often than most sites need, and plugins can make it run even more.

This article explains what Heartbeat does and which features depend on it, how to measure how much load it creates, how to slow it down with the heartbeat_settings filter, how to disable it on specific screens, the risks of removing it completely, and how plugins use it so you can audit what is actually running on your site.

What the Heartbeat API Does

Heartbeat, introduced in WordPress 3.6, is a JavaScript script (heartbeat.js) that sends a request to the server at a regular interval, called a tick. Each tick is a POST request to admin-ajax.php with action=heartbeat. WordPress core and plugins attach data to the outgoing request, the server processes it, and the response is passed back to JavaScript listeners in the browser.

Core uses Heartbeat for several important features:

FeatureWhat Heartbeat doesWhere it runs
Post lockingTells other users that someone is editing a post, and lets them take overPost editor
Autosave (classic editor)Sends the draft to the server so work is not lostClassic post editor
Login expirationDetects an expired session and shows a login modal instead of losing your workAll admin screens
Nonce refreshRefreshes security tokens on long-open screens so saves keep workingPost editor
Locked post indicatorsShows which posts in a list are being edited by someone elsePosts list screens

The block editor saves autosaves through the REST API rather than Heartbeat, but it still relies on Heartbeat for post locking and for refreshing nonces and login state.

Plugins use Heartbeat too. Page builders, ecommerce dashboards, security plugins, and live notification features commonly hook into it to push updates to the browser.

How Often Heartbeat Runs

Heartbeat is smarter than a fixed timer. The default interval is 60 seconds, and heartbeat.js adjusts that automatically:

  • When the browser tab is hidden or loses focus, the interval slows to 120 seconds. Post locks expire after 150 seconds, so 120 seconds is the slowest rate that keeps them alive.
  • After 5 minutes without mouse or keyboard activity, Heartbeat also slows to 120 seconds.
  • After 10 minutes of inactivity, Heartbeat suspends completely on most screens. It resumes as soon as the user moves the mouse or types.
  • On the post editor screens, post.php and post-new.php, WordPress disables suspension so post locks and session checks stay active while the editor is open. Even there, Heartbeat always suspends after 60 minutes of inactivity.
  • Code can request a temporary fast mode, a 5-second interval for a limited number of ticks, for example while waiting for a lock to be released.

In current WordPress, the interval can be set to any value from 1 to 3600 seconds. Older documentation often says 15 to 120 seconds, which reflected the limits in earlier versions.

The problem is rarely one tab. It is ten editors, each with several tabs open in the post editor all day, plus plugins that switch Heartbeat into fast mode or add expensive work to every tick.

Measuring Heartbeat Load Before Changing Anything

Do not disable Heartbeat because a performance checklist told you to. Measure first.

In the Browser

  1. Open a slow admin screen, such as the post editor.
  2. Open your browser's developer tools and go to the Network tab.
  3. Filter by admin-ajax.
  4. Watch the requests that appear. Click one and check the Payload tab for action: heartbeat, and the Timing tab for how long the server took.

If each tick takes over a second, something attached to Heartbeat is expensive.

In the Server Logs

Count Heartbeat-related requests in the access log. The action is in the POST body, which access logs do not record, so count all admin-ajax.php POSTs and compare with the browser check:

# Terminal
grep "POST /wp-admin/admin-ajax.php" /var/log/nginx/access.log | wc -l

# Requests per IP address, busiest first
grep "POST /wp-admin/admin-ajax.php" /var/log/nginx/access.log \
  | awk '{print $1}' | sort | uniq -c | sort -rn | head

With Query Monitor

Query Monitor shows the queries, hooks, and PHP errors for admin-ajax requests too. It helps identify which plugin attaches slow work to the heartbeat_received filter. The setup is covered in how to debug WordPress with WP_DEBUG and Query Monitor.

Slowing Heartbeat Down with heartbeat_settings

The safest change is to keep Heartbeat running but less often. The heartbeat_settings filter changes the settings passed to heartbeat.js when the page loads. Put the code in a small must-use plugin so it survives theme changes:

// wp-content/mu-plugins/heartbeat-control.php
<?php
/**
 * Plugin Name: Heartbeat Control
 * Description: Slows the WordPress Heartbeat API to reduce admin-ajax load.
 */

add_filter( 'heartbeat_settings', function ( $settings ) {
// Seconds between ticks while the tab is focused.
$settings['interval'] = 60;

return $settings;
} );

60 seconds is already the default, so this example is a template. Raise the value to reduce load. A sensible approach is to keep the editor at or below 120 seconds, so post locks keep working, and to slow down every other screen more:

// wp-content/mu-plugins/heartbeat-control.php
<?php
/**
 * Plugin Name: Heartbeat Control
 * Description: Per-screen Heartbeat intervals.
 */

add_filter( 'heartbeat_settings', function ( $settings ) {
global $pagenow;

if ( in_array( $pagenow, array( 'post.php', 'post-new.php' ), true ) ) {
// Editors: keep post locking reliable.
$settings['interval'] = 60;
} else {
// Dashboard, lists, settings pages.
$settings['interval'] = 120;
}

return $settings;
} );

Setting a Hard Minimum with minimalInterval

Plugins can call wp.heartbeat.interval( 'fast' ) from JavaScript, which overrides your interval setting for a while. If a plugin keeps doing that, use minimalInterval. It sets a floor that every other interval, including fast mode, cannot go below. It is set once when the page loads and cannot be changed later:

// wp-content/mu-plugins/heartbeat-control.php
add_filter( 'heartbeat_settings', function ( $settings ) {
// Never tick more often than every 30 seconds, whatever plugins request.
$settings['minimalInterval'] = 30;

return $settings;
} );

minimalInterval accepts values up to 600 seconds, but anything above 120 seconds breaks post locking, because locks expire after 150 seconds without a tick.

Disabling Heartbeat on Specific Screens

Some screens gain nothing from Heartbeat. On the front end of the site, Heartbeat only loads if a theme or plugin enqueues it. In the admin, the dashboard and list screens rarely need frequent ticks.

The heartbeat script is a registered dependency of other scripts, so the cleanest way to stop it is to deregister it on the screens where you do not want it:

// wp-content/mu-plugins/heartbeat-control.php
<?php
/**
 * Plugin Name: Heartbeat Control
 * Description: Disables Heartbeat outside the post editor.
 */

add_action( 'admin_enqueue_scripts', function ( $hook_suffix ) {
// Keep Heartbeat on the post editor for locking, autosave, and nonce refresh.
if ( in_array( $hook_suffix, array( 'post.php', 'post-new.php' ), true ) ) {
return;
}

wp_deregister_script( 'heartbeat' );
}, 1 );

// Front end: stop any theme or plugin from loading Heartbeat for visitors.
add_action( 'wp_enqueue_scripts', function () {
wp_deregister_script( 'heartbeat' );
}, 1 );

Running at priority 1 means the script is removed before other code tries to enqueue it. Scripts that list heartbeat as a dependency will not load either, because WordPress skips scripts with missing dependencies. That includes the login-expiration modal (wp-auth-check) on those screens. Test each admin screen your team uses after making this change, and check the browser console for errors.

What You Lose on the Editor Screens

If you also disable Heartbeat on post.php and post-new.php, these features stop working:

  • Post locking. Two editors can open the same post, and the last one to save silently overwrites the other's work.
  • Classic editor autosave. Unsaved changes are lost if the browser crashes or the tab closes.
  • Session expiry warnings. If your login expires, the next save fails instead of prompting you to log back in.
  • Nonce refresh. Editors who keep a post open for hours may hit "The link you followed has expired" when saving.

On a single-author blog, losing post locking might be acceptable. On any site with multiple editors, it is not. Slowing Heartbeat on the editor is safer than removing it.

Removing Heartbeat Entirely

You can deregister Heartbeat everywhere:

// wp-content/mu-plugins/disable-heartbeat.php
<?php
/**
 * Plugin Name: Disable Heartbeat
 * Description: Removes Heartbeat everywhere. Not recommended for multi-author sites.
 */

add_action( 'init', function () {
wp_deregister_script( 'heartbeat' );
}, 1 );

Do this only when you understand every feature you are giving up, and only after measuring that Heartbeat really is the bottleneck. In most cases, the real cause of a slow admin is a plugin doing expensive work on every tick, and fixing or replacing that plugin is better than disabling the system for everything else.

How Plugins Use Heartbeat

Knowing how Heartbeat is extended helps you audit what runs on each tick. On the browser side, scripts attach data before each tick and read the response afterwards:

// wp-content/plugins/acme-orders/assets/orders.js
jQuery(document).on("heartbeat-send", function (event, data) {
  data.acme_orders_check = true;
});

jQuery(document).on("heartbeat-tick", function (event, data) {
  if (data.acme_new_orders) {
    document.querySelector("#acme-order-count").textContent =
      data.acme_new_orders;
  }
});

On the server, the plugin responds through the heartbeat_received filter for logged-in users, or heartbeat_nopriv_received for logged-out visitors:

// wp-content/plugins/acme-orders/acme-orders.php
add_filter( 'heartbeat_received', function ( $response, $data, $screen_id ) {
if ( empty( $data['acme_orders_check'] ) || ! current_user_can( 'manage_options' ) ) {
return $response;
}

// Cache the count so each tick does not run a fresh query.
$count = get_transient( 'acme_new_order_count' );

if ( false === $count ) {
$count = acme_count_new_orders();
set_transient( 'acme_new_order_count', $count, MINUTE_IN_SECONDS );
}

$response['acme_new_orders'] = (int) $count;

return $response;
}, 10, 3 );

This example shows two habits worth checking for in any plugin: it returns early unless its own data was sent, and it caches expensive work instead of repeating it on every tick. A plugin that runs heavy queries on every heartbeat_received call, for every user, is the most common reason Heartbeat becomes a problem.

To find which plugins hook in, search the plugins folder:

# Terminal
grep -rl "heartbeat_received\|heartbeat-send\|heartbeat-tick" wp-content/plugins/

Plugin Options for Non-Developers

If you prefer not to write code, several plugins expose Heartbeat settings in the dashboard. Heartbeat Control by WP Rocket lets you disable, allow, or change the frequency of Heartbeat separately for the dashboard, the front end, and the post editor. Many caching and performance plugins include similar options. They all use the same filters and techniques described above, so the same cautions apply: keep the post editor at 120 seconds or faster if you have multiple editors.

Reducing Admin Load Beyond Heartbeat

Heartbeat is one of several sources of uncacheable admin requests. If load stays high after tuning it:

  • Use a persistent object cache such as Redis, so repeated queries in Heartbeat callbacks and admin screens hit memory instead of the database.
  • Replace WP-Cron's page-load trigger with a real server cron job, so scheduled tasks stop running during admin requests. See how to replace WP-Cron with a real server cron job.
  • Audit plugins for features you do not use. Live dashboards, real-time notifications, and analytics widgets on the dashboard are common culprits.
  • Upgrade PHP to a currently supported version for faster execution of every request.

For a wider view of performance problems, see what are the common reasons for a slow WordPress website.

Common Problems and Fixes

  • "Someone else is editing this post" warnings stop appearing. Heartbeat is disabled or slowed beyond 120 seconds on the editor. Restore it on post.php and post-new.php.
  • "The link you followed has expired" when saving after a long editing session. Nonce refresh depends on Heartbeat. Keep it enabled on the editor.
  • admin-ajax requests continue after setting a long interval. A plugin is switching Heartbeat to fast mode or making its own AJAX calls. Set minimalInterval, then check the Network tab for non-Heartbeat actions.
  • JavaScript errors after deregistering Heartbeat. A plugin script depends on it. Keep Heartbeat on that plugin's screens, or replace the plugin.
  • No change in server load. Heartbeat was not the cause. Use Query Monitor and server logs to find the real source of admin-ajax or REST traffic.

Heartbeat API FAQ

Disabling it on the front end and on admin screens other than the post editor is usually safe. Disabling it on the post editor removes post locking, classic editor autosave, session expiry warnings, and nonce refresh, which can cause lost work on sites with several editors.

The default is 60 seconds while the tab is focused. It slows to 120 seconds when the tab is hidden or the user is inactive for five minutes, and suspends after ten minutes of inactivity on screens other than the post editor.

Not by default. Core does not load Heartbeat on the front end. It only runs there if a theme or plugin enqueues it, which some page builders and live features do.

Yes, for post locking, login checks, and nonce refresh. The block editor sends autosaves through the REST API instead of Heartbeat, but it still needs Heartbeat to warn editors about each other.

Each admin-ajax request loads WordPress and all active plugins and cannot be page-cached. Frequent Heartbeat ticks from many open tabs, or plugins doing expensive work on each tick, multiply that cost. Measure which actions are called before deciding what to change.

Conclusion

The Heartbeat API keeps the WordPress admin safe for collaboration. It protects posts from being overwritten, keeps sessions and nonces fresh, and powers autosave in the classic editor. It also sends an uncacheable request to the server every minute from every open admin tab, and plugins can make that much worse.

Measure before you change anything. Then slow Heartbeat down with the heartbeat_settings filter, set a minimalInterval floor if plugins keep speeding it up, and deregister it only on screens that do not need it. Keep it running on the post editor at 120 seconds or faster on any site with more than one editor, and look for plugins doing heavy work on every tick, because that is usually the real cause.

Here are some useful references for going deeper on the Heartbeat API:

  1. WordPress Developer Docs: Heartbeat API — how to send and receive data with Heartbeat in plugins.
  2. WordPress Developer Docs: heartbeat_settings filter — the filter for changing Heartbeat settings in PHP.
  3. WordPress Developer Docs: heartbeat_received filter — the server-side hook for Heartbeat responses.
  4. WordPress Developer Docs: wp_deregister_script() — removing a registered script.
  5. MDN Web Docs: Page Visibility API — the browser API Heartbeat uses to slow down in background tabs.
Tags :
Share :

Related Posts

WordPress optimization with specific recommended approach

WordPress optimization with specific recommended approach

Whether you run a high traffic WordPress installation or a small blog on a low cost shared host, you should optimize WordPress and your server to run

Continue Reading
Creating and Customizing WordPress Child Themes

Creating and Customizing WordPress Child Themes

Creating a child theme in WordPress is a best practice for making modifications to a theme. By using a child theme, you can update the parent theme w

Continue Reading
Understanding the Distinction Categories vs. Tags in WordPress

Understanding the Distinction Categories vs. Tags in WordPress

WordPress, a powerful content management system, offers a plethora of features to organize content effectively. Among these features, categories and

Continue Reading