RevealTheme logo
Back to Blog

The Real Cost of WordPress Heartbeat

The Real Cost of WordPress Heartbeat
The RevealTheme Team

By

·

WordPress Heartbeat is one of those features almost nobody chooses to turn on, yet it runs on practically every site quietly burning CPU cycles in the background. It is the API responsible for autosaving your posts, warning you when another user is editing the same content, and keeping the post-lock and login session alive while you work. Useful stuff. But the way it is implemented — a recurring browser-to-server poll — means it has a measurable cost that scales with how many people are logged in and how long they leave a tab open. On a busy WooCommerce store or a multi-author publication, that cost is not theoretical.

What Heartbeat actually does under the hood

Heartbeat is a JavaScript timer (wp-includes/js/heartbeat.js) that fires an AJAX request to /wp-admin/admin-ajax.php with the action heartbeat. Every time it fires, WordPress bootstraps a near-full request: it loads wp-load.php, initializes the whole plugin and theme stack, runs the heartbeat hooks, and returns a JSON payload. The key detail is that admin-ajax.php is not cached. It cannot be — it is dynamic, authenticated, and unique per user. So every single tick is a full, uncached PHP execution that hits your database.

The default intervals are the part most people get wrong when they estimate the impact:

  • Post editor: 15 seconds. While you have a post open in the block editor, your browser pings the server roughly every 15 seconds.
  • WordPress dashboard (other admin pages): 60 seconds.
  • Front end: Heartbeat does not run for logged-out visitors at all by default. It only loads when a logged-in user is on the front end (for example, with the admin bar showing).

WordPress also slows the interval automatically when a tab loses focus — after about five minutes of inactivity it can back off to a 120-second interval. That mitigation helps, but it does not eliminate the cost, and it does nothing for active tabs.

The real cost, made concrete

Let's do the arithmetic instead of hand-waving. A single author with one post open for an 8-hour day, ticking every 15 seconds, generates roughly 1,900 uncached admin-ajax requests in that day. Each one is a full PHP bootstrap plus several database queries. Now put ten authors in your editorial team, each with a couple of tabs open, and you are looking at tens of thousands of uncached dynamic requests per day that produce zero visitor-facing value.

Where this bites hardest:

  • Shared and budget hosting. Many shared hosts cap concurrent PHP workers (often 1–4 on entry plans). A flurry of Heartbeat requests can occupy those workers, queueing real visitor traffic behind them. This is the classic "my site feels slow but the front end is cached" complaint.
  • CPU-metered hosting. Hosts like Cloudways, Kinsta, and WP Engine bill or throttle on resource usage. Heartbeat counts. On Kinsta and WP Engine specifically, admin-ajax Heartbeat traffic shows up clearly in the analytics as a top "URL by requests," and it is excluded from page caching by design.
  • WooCommerce stores. Logged-in customers and, worse, multiple staff in the order screen generate steady Heartbeat load against an already query-heavy database.
  • Object-cache and DB pressure. Each tick runs authenticated queries. At scale this inflates your database connections and can blow through a small Redis or MySQL connection budget.

To be fair about the other side: on a low-traffic blog with one occasional author, the cost is negligible — fractions of a percent of CPU. Disabling Heartbeat there is premature optimization. The cost only becomes "real" with concurrency.

How to measure it on your own site

Don't guess. Open your hosting analytics or server access logs and grep for admin-ajax.php. On Kinsta and WP Engine the dashboard surfaces top requested URLs directly; on a VPS, run something like grep "admin-ajax.php" access.log | wc -l for a day and compare it to your total request count. If admin-ajax is in your top three requested URLs and you have more than a handful of logged-in users, Heartbeat is worth tuning. Query Monitor will also show you the cost of an individual heartbeat request — install it, open a post editor, and watch the queries and time per tick.

How to control Heartbeat without breaking things

The wrong move is to nuke Heartbeat entirely with wp_deregister_script('heartbeat'). Do that and you lose autosave and the "another user is editing this post" lock — a genuine recipe for lost work and overwrites on a multi-author site. Tune it instead.

Option 1: The Heartbeat Control plugin (easiest)

The free Heartbeat Control plugin lets you, per location, either disable, allow, or change the frequency of Heartbeat. The sensible configuration for most sites:

  • Post editor: keep enabled but raise the interval to 30–60 seconds. You still get autosave and post locking, at half to a quarter of the request volume.
  • Dashboard / admin pages: raise to 120 seconds or disable if you don't rely on live notification counts.
  • Front end: disable entirely — there is almost never a reason for logged-in front-end Heartbeat.

Option 2: Code in your theme or a small plugin

If you'd rather not add a plugin, change the interval with a filter. Raising it to 60 seconds in the editor cuts editor Heartbeat traffic by 75%:

add_filter( 'heartbeat_settings', function( $settings ) {
    $settings['interval'] = 60; // seconds; valid range is 15–120
    return $settings;
} );

To kill Heartbeat only on the front end while leaving the editor untouched:

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

Option 3: Performance plugins you may already have

Several caching and optimization plugins fold Heartbeat control into their UI, so you might not need a separate tool. WP Rocket has a dedicated Heartbeat tab (Reduce / Disable, per context). Perfmatters exposes the same interval controls. If you run either, configure it there rather than stacking plugins.

A sane default policy

Here is the configuration I'd reach for on a typical content or commerce site with multiple logged-in users:

  1. Editor Heartbeat: enabled at 30–60 seconds (preserve autosave and post locking).
  2. Admin dashboard Heartbeat: 120 seconds, or disabled if no live counts are needed.
  3. Front-end Heartbeat: disabled.
  4. Re-check admin-ajax volume in your host analytics a week later to confirm the drop.

The goal is not to eliminate Heartbeat — it does real work that protects your editors from losing content. The goal is to stop paying for ticks you don't need: 15-second editor polling that could be 60, dashboard polling you never look at, and front-end polling that serves no purpose at all. Done right, you keep every feature that matters and shave a meaningful chunk of uncached PHP load off your busiest, most expensive request path.