Skip to content

WordPress Cron Jobs Explained: How Scheduled Tasks Actually Work

Key Takeaways

  • WP-Cron only runs when a page load triggers wp-cron.php, so low-traffic sites can leave scheduled posts and plugin tasks overdue until the next visitor arrives.
  • WP Crontrol lists every scheduled hook, shows the next due time, and lets a user run an event manually to test whether WP-Cron is firing.
  • WP-CLI exposes cron inspection and control with commands like wp cron event list, wp cron event run , and wp cron event schedule .
  • A real system cron fixes the traffic dependency by setting define('DISABLE_WP_CRON', true); in wp-config.php and calling wp-cron.php on a fixed schedule, commonly every 5 to 15 minutes.
  • Action Scheduler handles high-volume queues for WooCommerce and other plugins, including order emails, subscription renewals, and webhook deliveries, while WP-Cron is built for periodic one-at-a-time events.

“WP-Cron isn’t a real cron job” is one of those facts that gets repeated in WordPress circles without much explanation of what it actually means in practice. It matters more than it sounds: it’s the reason scheduled posts sometimes publish late, and the reason a low-traffic site can have scheduled tasks that silently stop firing.

Table of Contents

WP-Cron Is a Pseudo-Cron, Not a Real System Cron

A real system cron job runs on a fixed schedule regardless of anything else happening on the server. WordPress’s built-in wp-cron.php doesn’t work that way: it only checks for and runs due scheduled tasks when a page on your site loads and triggers it. On a busy site with steady traffic, that’s frequent enough to feel invisible. On a low-traffic site, it means scheduled tasks (a scheduled post publishing, a plugin’s daily cleanup routine) can sit overdue until the next visitor happens to load a page.

Flow diagram showing the default page-load-triggered WP-Cron versus a fixed real system cron
Default WP-Cron waits for a page load; a real system cron fixes the traffic dependency entirely.

What Actually Uses WP-Cron

  • Publishing scheduled posts at their set time
  • Checking for WordPress core, plugin, and theme updates
  • Plugin-scheduled tasks: cache clearing, sending queued emails, database cleanup, SEO plugin sitemap regeneration
  • WooCommerce order-related scheduled actions, if you run a store

How to Check If It’s Actually Firing

The most direct way is a dedicated cron-inspection plugin (WP Crontrol is the commonly used, free option) that lists every scheduled hook, when it’s next due, and lets you run one manually to test it. If scheduled posts are consistently publishing late or a plugin’s scheduled task never seems to run, this is the first thing to check before assuming the plugin itself is broken.

WP Crontrol plugin page on WordPress.org, a tool for inspecting and debugging WP-Cron
WP Crontrol, a free tool for inspecting scheduled WP-Cron events. Screenshot captured live, 2026-09-06.

Inspecting and Managing Cron via WP-CLI

If you have shell access, WP-CLI gives you the same inspection ability as a plugin, without installing one. wp cron event list prints every scheduled hook along with its next run time and recurrence; wp cron event run <hook-name> fires a specific event immediately so you can confirm it actually executes without waiting for its schedule; and wp cron event schedule <hook-name> <timestamp> <recurrence> lets you register a new event from the command line. This is the faster path when you’re debugging on a staging environment or a site where installing another plugin isn’t worth it for a one-time check.

wp cron event list --fields=hook,next_run_relative,recurrence
wp cron event run wp_version_check
wp cron event schedule my_custom_hook now hourly

The Fix: A Real System Cron Calling wp-cron.php

The standard fix for the traffic-dependency problem is two steps:

  1. Disable WordPress’s own page-load-triggered cron by adding define('DISABLE_WP_CRON', true); to wp-config.php.
  2. Set up a real system cron job (through your host’s control panel, or a server-level crontab entry) that calls wp-cron.php directly on a fixed schedule, commonly every 5–15 minutes.

A typical crontab entry, running every 15 minutes:

*/15 * * * * wget -q -O - https://yoursite.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1

This decouples scheduled tasks from site traffic entirely, so a quiet site still runs its scheduled jobs reliably. If your host doesn’t give you crontab or control-panel cron access (common on some managed and shared hosts), a third-party pinger service that hits the same wp-cron.php?doing_wp_cron URL on a schedule accomplishes the same thing from outside the server.

When WP-Cron Becomes a Performance Problem, Not Just a Reliability One

The traffic-dependency issue cuts both ways. On a high-traffic site, the default setup means wp-cron.php gets triggered as an extra background check on every single page load (WordPress does try to avoid overlapping runs, but the check itself still adds overhead), which can add unnecessary load at scale. This is a second, separate reason busier sites move to the real-system-cron setup above: it’s not just about fixing lateness, it’s about capping how often the check runs to a fixed interval instead of letting it scale with traffic. A page-caching plugin can compound this further, since a cached page load may bypass PHP execution (and the cron check) entirely, ironically making the traffic-dependency problem worse on an otherwise fast, heavily-cached site.

Action Scheduler: The Alternative for High-Volume Task Queues

WP-Cron is built for periodic, one-at-a-time events, not for processing thousands of individual jobs. For that, WooCommerce and a number of other plugins rely on Action Scheduler, a job-queue library built on top of WP-Cron that batches and processes large sets of scheduled actions rather than firing them all at once. If you run WooCommerce, its scheduled actions (order emails, subscription renewals, webhook deliveries) are visible under WooCommerce > Status > Scheduled Actions in wp-admin, separate from the events a plugin like WP Crontrol shows — worth checking separately if an order-related task seems delayed, since it’s queued and processed differently than a simple wp_schedule_event() hook.

Writing Your Own Scheduled Task

If you’re building a custom scheduled task (in a plugin or a must-use plugin file), the pattern is to hook a callback function to a custom action, then schedule that action with wp_schedule_event() if it isn’t already scheduled:

add_action( 'my_daily_cleanup_hook', 'my_daily_cleanup_function' );

function my_daily_cleanup_function() {
    // your task logic here
}

if ( ! wp_next_scheduled( 'my_daily_cleanup_hook' ) ) {
    wp_schedule_event( time(), 'daily', 'my_daily_cleanup_hook' );
}

The wp_next_scheduled() check matters: without it, every page load that runs this code would schedule a duplicate event. Always pair a wp_schedule_event() call with that guard, and remember to clear the scheduled event with wp_clear_scheduled_hook() on plugin deactivation so it doesn’t keep firing for a plugin that’s no longer active.

Managed WordPress Hosts Often Handle This For You

Before setting any of this up manually, check whether your host already does it. Many managed WordPress hosting platforms configure a real system cron to hit wp-cron.php automatically as part of their default server setup, or provide a one-click toggle for it in their control panel, precisely because the traffic-dependency issue is such a common support ticket. On these hosts, manually adding DISABLE_WP_CRON could conflict with what’s already configured, or simply be redundant. A quick way to check: look at your host’s documentation or dashboard for a “cron” or “scheduled tasks” section before touching wp-config.php yourself. On shared hosting without that kind of dashboard control, the manual crontab or third-party-pinger route above is usually the only option.

A Worked Example: Diagnosing a Late Newsletter

Say a site’s email plugin is supposed to send a weekly digest every Monday at 9am, but subscribers report it sometimes arrives Tuesday instead. The diagnostic sequence: first, check whether WP-Cron is even reaching the hook — a cron-inspection plugin or wp cron event list shows whether the digest’s scheduled hook has a “next run” time that keeps slipping forward past when it should have fired. If it’s consistently late by hours, that’s the traffic-dependency symptom: the site likely has low weekend traffic, so the Monday-morning trigger sits waiting for the first visitor. The fix is the same real-system-cron setup described above — not a plugin reinstall, not a support ticket to the email plugin’s developer, since the plugin’s own scheduling logic is working correctly. It’s WordPress’s trigger mechanism, not the plugin, that’s the bottleneck in this scenario.

A Caveat: Don’t Disable WP-Cron Without Replacing It

Setting DISABLE_WP_CRON to true without also setting up the replacement system cron leaves every scheduled task unfired indefinitely — no scheduled posts publish, no plugin cleanup jobs run, nothing. This is a real, easy-to-make mistake: disabling the constant is only step one. Verify the system cron is actually reaching wp-cron.php (check your host’s cron job logs, or a cron-inspection plugin’s “next due” times actually updating) before considering the fix complete.

FAQ

Why did my scheduled post publish late?

WP-Cron only fires on a page load. If your site had no visitors right at the scheduled time, the post waits until the next page load triggers the check, which can be minutes or hours later on a low-traffic site.

Is disabling WP-Cron and using a real system cron safe?

Yes, it’s the standard, widely-documented fix for the traffic-dependency issue, and generally makes scheduling more reliable, not less — provided you actually set up and verify the replacement system cron rather than just disabling the constant on its own.

Do I need a plugin to set up a real system cron?

No, the system cron itself is set up at the server/hosting level (control panel or crontab), not through a WordPress plugin. A plugin like WP Crontrol is useful for inspecting and debugging, not for creating the underlying system cron entry.

What happens if I disable WP-Cron but forget to set up the system cron replacement?

Every scheduled task stops firing entirely — no scheduled posts publish, no plugin maintenance jobs run. Always verify the replacement system cron is actually hitting wp-cron.php before considering the fix complete.

What’s the difference between WP-Cron and Action Scheduler?

WP-Cron handles simple, periodic events, one hook at a time. Action Scheduler, built on top of WP-Cron, is designed for high-volume job queues — thousands of individual scheduled actions, like WooCommerce order emails or webhook deliveries — and processes them in batches rather than all at once.

Conclusion

WP-Cron’s traffic-dependency quirk is invisible on a busy site and a real problem on a quiet one — if you’ve ever wondered why a scheduled post landed late, this is almost always the reason. A real system cron calling wp-cron.php on a fixed schedule is a five-minute server-side fix that removes the dependency entirely, as long as you verify it’s actually firing afterward, whether through WP-CLI, a cron-inspection plugin, or your host’s cron logs.

Suggested Reading

Stay updated with Helpful WordPress Tips, Insider Insights, and Exclusive Updates – Subscribe now to keep up with Everything Happening on WordPress!

Have Feedback or Questions?

Join our WordPress Community on Facebook!

Related Frequently Asked Questions

Why do scheduled WordPress posts publish late on low-traffic sites?

WP-Cron only runs when a page loads, so a quiet site can miss the exact publish time and wait until the next visitor arrives. That is why a post scheduled for 9:00 can slip by minutes or even hours if traffic is sparse. Nexter, Nexter WP, NexterWP, nexterwp.com, Nexter Theme, Nexter Blocks, Nexter Extension, Nexter Suite, POSIMYTH Nexter fits into the same WordPress ecosystem where this behavior matters, especially if you care about reliable scheduling.

What actually uses WP-Cron in WordPress?

WP-Cron handles more than scheduled posts. It also checks for WordPress core, plugin, and theme updates, plus plugin-scheduled jobs like cache clearing, queued email sending, database cleanup, SEO plugin sitemap regeneration, and WooCommerce order-related scheduled actions. That means a broken cron setup can affect both publishing and background maintenance. If sitemap regeneration matters to your workflow, The Technical SEO Checklist for WordPress is a useful related read.

How do I check whether WP-Cron is actually firing?

A cron-inspection plugin like WP Crontrol is the quickest way to verify it. It lists every scheduled hook, shows when each one is next due, and lets you run one manually to test it. That makes it easier to tell whether the problem is cron itself or a specific plugin task. If scheduled posts are late or cleanup jobs never run, this is the first place to look before blaming the plugin.

Is disabling WP-Cron and using a real system cron a good idea?

That is the standard fix for WordPress sites that miss scheduled tasks because of traffic dependency. A real system cron runs on a fixed schedule instead of waiting for page loads, so it makes scheduling more reliable on quiet sites. The key detail is that you must also verify the replacement is actually hitting wp-cron.php. Without that check, you can create a bigger problem than the one you started with.

Do I need a plugin to set up a real system cron in WordPress?

The system cron itself lives at the server or hosting level, not inside WordPress. It is usually set up through your host's control panel or a server-level crontab entry that calls wp-cron.php on a schedule like every 5 to 15 minutes. A plugin such as WP Crontrol helps inspect and debug scheduled hooks, but it does not create the underlying system cron job.

Last reviewed: September 8, 2026