WP-Cron Explained: How It Works, When to Disable It, and Using a Real Cron Job

How WP-Cron works, why scheduled tasks run late on low-traffic sites, how to disable it and use a real server cron job, and how to list, debug and schedule events.

WP-Cron is WordPress’s built-in task scheduler: plugins and core register events (publishing scheduled posts, sending emails, running backups, processing WooCommerce actions), and WordPress checks for due events on page loads, running them through a background request to wp-cron.php. Because it depends on visits, it is not precise; a real server cron job fixes that.

This guide is part of my automation series.

How does WP-Cron work?

On each page load, WordPress checks whether any scheduled event is due. If so, it sends a non-blocking request to wp-cron.php, which runs the due events. No visits means no checks, so tasks run late on quiet sites. Events are stored in the cron option in the database.

Problems with the default behaviour

  • Missed schedules on low-traffic sites: a post scheduled for 9:00 publishes when the next visitor arrives.
  • Page caching: if a full-page cache serves pages without running PHP, WordPress may not trigger cron from those visits.
  • Loopback failures: some hosts or firewalls block the site from requesting itself, so wp-cron.php never runs. Site Health reports this.
  • Load on busy sites: frequent checks, and long tasks running inside web requests.
  • WooCommerce dependence: Action Scheduler, which WooCommerce uses for webhooks, emails and background jobs, relies on WP-Cron by default (plus async requests).

Step 1: see what is scheduled

With WP-CLI:

wp cron event list --fields=hook,next_run_relative,recurrence
wp cron schedule list
wp cron test

Or install the WP Crontrol plugin to view, run and edit events in the admin. Look for:

  • Events overdue by a long time.
  • Hooks from plugins you removed (orphaned events).
  • Very frequent schedules that do heavy work.

Step 2: disable the page-load trigger

In wp-config.php, above the “That’s all, stop editing” line:

define( 'DISABLE_WP_CRON', true );

This only stops visits from triggering cron. Events still exist and still need to run, so do step 3 immediately.

Step 3: add a real cron job

In your hosting control panel (cPanel, hPanel and similar have a “Cron Jobs” section) or with crontab -e on a server.

Option A: WP-CLI (preferred when available)

*/5 * * * * cd /home/user/public_html && wp cron event run --due-now --quiet

Option B: request wp-cron.php

*/5 * * * * curl -s https://example.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1

Option C: PHP directly

*/5 * * * * cd /home/user/public_html && php wp-cron.php > /dev/null 2>&1

WP-CLI and direct PHP avoid going through the web server, firewall and cache, which makes them more reliable. Make sure the cron job runs as the same user that owns the site files.

Step 4: verify

  • Schedule a test post a few minutes ahead and check it publishes.
  • Run wp cron event list and confirm no events are long overdue.
  • Check Tools → Scheduled Actions in WooCommerce for pending actions piling up.
  • Check Site Health for scheduled event warnings.

Scheduling your own events

In a plugin:

register_activation_hook( __FILE__, function () {
    if ( ! wp_next_scheduled( 'mx_daily_cleanup' ) ) {
        wp_schedule_event( time() + HOUR_IN_SECONDS, 'daily', 'mx_daily_cleanup' );
    }
} );

register_deactivation_hook( __FILE__, function () {
    wp_clear_scheduled_hook( 'mx_daily_cleanup' );
} );

add_action( 'mx_daily_cleanup', function () {
    // Keep it short; move heavy work into batches.
    delete_expired_transients();
} );

Good habits:

  • Schedule on activation, clear on deactivation; do not call wp_schedule_event() on every page load without checking.
  • Keep each run short; process large jobs in batches across runs.
  • Make tasks safe to run twice.
  • Log failures somewhere you will see them.

Custom intervals

add_filter( 'cron_schedules', function ( $schedules ) {
    $schedules['every_fifteen_minutes'] = array(
        'interval' => 15 * MINUTE_IN_SECONDS,
        'display'  => __( 'Every 15 minutes', 'mx' ),
    );
    return $schedules;
} );

Remember: WP-Cron intervals are minimums, not guarantees. With a 5-minute server cron, a 1-minute schedule still runs about every 5 minutes.

Real cron for heavier automation

For tasks like supplier CSV imports, sitemap pings or report generation, I often skip WP-Cron and call a custom WP-CLI command directly from the server cron at a fixed time. It is explicit, logged and independent of WordPress’s queue.

Checklist

  • Scheduled events reviewed; orphaned hooks removed
  • DISABLE_WP_CRON set and a server cron job added
  • Cron job tested with a scheduled post
  • WooCommerce scheduled actions not piling up
  • Custom events scheduled on activation and cleared on deactivation

Common mistakes

  • Setting DISABLE_WP_CRON and forgetting the server cron: emails, backups and scheduled posts silently stop.
  • Running the server cron every minute on a slow shared host with heavy tasks.
  • Leaving events from deleted plugins firing errors.
  • Doing heavy imports inside a single cron run that times out.

If scheduled tasks on your site are unreliable, contact me.

Frequently asked questions

What is WP-Cron?

WP-Cron is WordPress's built-in scheduler. It stores scheduled events in the database and runs any that are due when a page is loaded, by sending a background request to wp-cron.php.

Should I disable WP-Cron?

Disable the page-load trigger only if you replace it with a real server cron job. Otherwise scheduled posts, emails, backups and WooCommerce tasks stop running.

How often should the real cron job run?

Every 5 minutes suits most sites. Busy stores with time-sensitive tasks may use every minute; very quiet sites may use 15 minutes.

Why are my scheduled posts missing schedule?

Usually WP-Cron is not running: low traffic, DISABLE_WP_CRON set without a server cron, loopback requests blocked, or caching preventing PHP from running.

Muhamed Ahmed

Ecommerce & Web Solutions Developer

I'm Muhamed Ahmed, an ecommerce and web solutions developer with 15 years of experience building fast, secure online stores and websites on WooCommerce, WordPress and custom Laravel platforms. I write about website speed, SEO, automation and conversion.

All articles Work with me

Related articles