WordPress Cron Isn’t Real Cron — Why It Misfires, and How to Fix It

Date:

WordPress has its own task scheduler, WP-Cron, and most people never think about it until scheduled posts publish late or a busy site’s CPU spikes for no obvious reason. The root cause is the same in both cases: WP-Cron isn’t a real cron job. It’s faked with page loads — and that assumption breaks at both ends of the traffic scale.

The short answer: WP-Cron is WordPress’s built-in scheduler for tasks like publishing scheduled posts, checking for updates, and running plugin jobs. Unlike a real system cron, it isn’t driven by a clock — it’s triggered by page loads. That makes it self-defeating at both extremes: on a low-traffic site, tasks run late or not at all because no one visited to trigger them; on a high-traffic site, it fires on nearly every request and wastes CPU. The fix is to disable WP-Cron and run it from a real server cron on a fixed schedule — reliable and cheaper.

Despite the name, WP-Cron is named after the Unix cron scheduler, not “chronograph” — and the “pseudo” part matters. A real cron job is fired by the operating system’s clock at exact times, whether or not anyone is using the machine. WP-Cron has no clock of its own; it hitches a ride on visitor traffic.

Two ways to trigger the schedulerMove the trigger from page loads to a clock and both failure modes disappear
Default WP-Cron

Trigger = page loads. Low traffic: tasks run late or never (no visitor). High traffic: fires on nearly every request — wasted CPU.

misfires both ways
Disabled + system cron

Trigger = the OS clock, on a fixed interval (e.g. every 15 min), independent of traffic. Punctual on quiet sites, no per-visit overhead.

What WP-Cron actually is

WP-Cron is WordPress’s scheduling system. It runs the time-based jobs the platform and your plugins depend on:

  • publishing scheduled posts at their set time,
  • checking for core, plugin and theme updates,
  • running recurring plugin and theme tasks (backups, syncs, cache warming, email digests),
  • housekeeping like clearing transients.

The catch is how it fires. On each page load, WordPress checks whether any scheduled task is due and, if so, runs it in the background. There’s no separate timer — the page load is the timer.

WP-Cron has no clock. A page load is its only heartbeat — which is exactly why it’s unreliable at both traffic extremes.

Why page-load triggering breaks — at both ends

Tying the scheduler to traffic creates two opposite problems.

On a low-traffic site, tasks run late or never. If nobody visits between 2pm and 6pm, a task scheduled for 2pm doesn’t run until the next visitor arrives. Scheduled posts publish late, updates lag, and time-sensitive jobs drift. The scheduler is only as punctual as your traffic.

On a high-traffic site, it’s pure overhead. Now the opposite happens: WP-Cron fires on nearly every request. Each check spends PHP CPU and memory deciding whether anything is due, and it adds work to page loads that should be fast. At scale, that overhead is measurable — one more reason a busy site feels sluggish. It’s a classic entry in any WordPress speed audit.

The scheduler is only as punctual as your traffic — and only as cheap as your traffic is light.

The fix: disable WP-Cron and use a real system cron

The standard fix — documented by WordPress itself — is to stop WordPress from triggering cron on page loads and instead call it on a fixed schedule from the server’s real cron. Two steps:

1. Turn off the page-load trigger in wp-config.php, above the “stop editing” line:

define( 'DISABLE_WP_CRON', true );Code language: PHP (php)

2. Call WP-Cron on a schedule from your server’s crontab (or your host’s control-panel cron). Every 15 minutes is a sensible default:

*/15 * * * * curl -s https://example.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1Code language: Bash (bash)

Now the scheduler runs on a clock, independent of traffic: quiet sites stay punctual, and busy sites stop paying the per-request tax. On WP-CLI hosts you can call wp cron event run --due-now from cron instead of hitting the URL, which avoids the extra HTTP request entirely.

If you’d rather not touch the server

Editing wp-config.php and a crontab is the cleanest fix, but not the only one:

  • A cron-manager plugin gives you a UI to see every scheduled event, spot runaway or duplicate jobs, and change frequencies — useful for diagnosing what’s scheduled even if you keep the default trigger. Since it’s one focused job, it’s a fair candidate for a code snippet rather than a plugin if you only need the scheduling change.
  • Managed WordPress hosting typically disables WP-Cron for you and wires a real system cron at the platform level, so cron “just works” without you configuring anything.

The short version

WP-Cron is WordPress’s task scheduler, but it’s fired by page loads rather than a clock, so it misbehaves at both traffic extremes: scheduled posts and jobs run late on quiet sites (no visitor to trigger them) and it wastes CPU on busy ones (firing on nearly every request). The reliable fix is to set define( 'DISABLE_WP_CRON', true ); in wp-config.php and call wp-cron.php from a real server cron on a fixed interval such as every 15 minutes — punctual on quiet sites, no per-visit overhead on busy ones. A cron-manager plugin helps you see what’s scheduled, and managed hosts usually set this up for you.

Frequently Asked Questions

Is WP-Cron a real cron job?

No. A real cron job is fired by the operating system’s clock at set times regardless of activity. WP-Cron is a “pseudo-cron” triggered by WordPress page loads, so it only runs when someone visits your site — which is why it’s unreliable on low-traffic sites and wasteful on high-traffic ones.

Why do my scheduled posts publish late?

Almost always because WP-Cron didn’t fire on time. With no visitor between the scheduled time and later, nothing triggered the scheduler, so the post waited until the next page load. Running WP-Cron from a real system cron on a fixed interval fixes this.

Does disabling WP-Cron break scheduled tasks?

No — as long as you replace it. Setting DISABLE_WP_CRON only turns off the page-load trigger; you then call wp-cron.php from a server cron (for example every 15 minutes) so the same tasks run on a reliable schedule instead of a traffic-dependent one.

How often should the server cron run WP-Cron?

Every 15 minutes suits most sites — frequent enough that scheduled posts and jobs feel timely, infrequent enough to add negligible load. If you rely on minute-level precision, run it every 5 minutes; if you have very few scheduled tasks, hourly is fine.

Will this speed up my site?

On a high-traffic site, yes, modestly — you remove the cron check from nearly every page load. It won’t transform a slow site on its own, but it’s a clean win and a standard step once you’ve handled the bigger bottlenecks in a speed audit.

Share post:

Subscribe

Popular

More like this
Related

How to Merge Spreadsheets by URL (and Why Our Tool Reported 5 Misses That Weren’t)

Matching two spreadsheets on a URL column is the same job as matching a stock list to a price list by product code. It goes wrong in the same two places: how you clean the code, and how you count what didn't match.

Many-to-One: Why a Mapping Backlog Outgrows Its Target Taxonomy

You're mapping a messy source vocabulary onto a clean target taxonomy. The target has about 5,600 slots, so the job must be roughly 5,600 decisions — right? The backlog is already past 8,900, and still climbing.

The Fix That Didn’t Take: When a Content-Hash Gate Skips Your Code Change

You shipped a fix to how one field is computed, re-ran the pipeline across everything, and it reported a clean run. Then the consistency check still flags those records as wrong. The fix is in the code, the rebuild "ran" — and the records never changed. Your own optimisation skipped them.

Your Dedup Scan Is Over-Counting — Because It Forgot What You Already Fixed

The report said 80 duplicate groups still to resolve. You'd been resolving them for days. You run it again after a big batch and it *still* says 80-ish. The backlog isn't stubborn — the scan is counting work you already finished.