WordPress Cron Jobs: How to Set Them Up and Fix Them When They Break

Tested: WordPress 6.7 Β· PHP 8.2 Β· WP-CLI 2.10  |  Updated: March 2026  |  By: Liza Kliko

Quick Answer β€” What Is a WordPress Cron Job?

A WordPress cron job is a scheduled task that runs automatically in the background β€” things like checking for plugin updates, sending scheduled emails, publishing scheduled posts, and clearing expired transients. Unlike a real server cron job, WordPress cron (called WP-Cron) doesn’t run on a fixed schedule. Instead, it triggers on page visits: every time someone loads a page on your site, WordPress checks whether any scheduled tasks are due and runs them.

This visitor-triggered design means WP-Cron doesn’t run on low-traffic sites β€” if nobody visits your site at 3am, your 3am scheduled task won’t fire. For reliable task scheduling on WordPress, the solution is to disable WP-Cron and replace it with a real server-side cron job.

TL;DR

WordPress Cron Jobs β€” Everything in One Place

How WP-Cron Works
  • Triggers on page visits β€” not on a real time schedule
  • Lives in wp-cron.php in your WordPress root
  • Runs tasks registered by WordPress core, themes, and plugins
  • Unreliable on low-traffic sites β€” tasks fire late or not at all
  • Can cause performance spikes on high-traffic sites
When WP-Cron Breaks
  • Scheduled posts don’t publish on time
  • Backup plugins miss scheduled runs
  • WooCommerce order emails send late
  • Plugin update checks stop running
  • Expired transients accumulate in the database
Fix: disable WP-Cron in wp-config.php, then add a real server cron job pointing to wp-cron.php. Run it every 5–15 minutes. Takes 10 minutes to set up, solves cron problems permanently.

WordPress cron jobs are one of those topics where the gap between how they’re supposed to work and how they actually work on most sites is large enough to cause real problems β€” missed backups, delayed emails, scheduled posts that never go live, plugin update checks that silently stop running.

I’ve debugged WP-Cron on dozens of sites. The problems always trace back to the same root issue: most developers set up WordPress, leave WP-Cron on its default configuration, and assume it works like a real cron job. It doesn’t β€” and on any site that isn’t receiving constant traffic, it’s unreliable by design.

This guide covers what WP-Cron actually is, how to view every scheduled task currently registered on your site, how to diagnose when it stops working, how to replace it with a proper server cron job on every major host, and how to register your own custom cron events in WordPress.

How It Works

How WordPress Cron Actually Works (And Why It’s Not a Real Cron)

A traditional server cron job is managed by the operating system. You tell the server to run a specific command at a specific time, and the server does it β€” regardless of traffic, user activity, or anything else. It is deterministic and reliable.

WP-Cron works completely differently. Here is what actually happens on every page load on a WordPress site:

βš™οΈ The WP-Cron Request Lifecycle
  1. A visitor (or a bot, or Googlebot) loads any page on your site
  2. WordPress processes the request and, near the end of execution, calls wp_cron()
  3. wp_cron() checks the cron option in the WordPress database β€” a serialized array of all scheduled events and their next-run timestamps
  4. If any events are overdue (their next-run timestamp is in the past), WordPress spawns a non-blocking HTTP request to yourdomain.com/wp-cron.php
  5. wp-cron.php runs the overdue events in the background
  6. The visitor’s page loads normally β€” they don’t wait for the cron jobs to finish

The critical implication of step 1: if no page is loaded, wp-cron.php is never called. A scheduled post set to publish at 3:00 AM will publish whenever the next page visit happens after 3:00 AM β€” which on a low-traffic blog might be 3:47 AM, or 9:15 AM, or not until the next day.

What Tasks Are Registered by Default?

A fresh WordPress installation with a standard plugin stack registers roughly 15–30 cron events. Here are the core ones you’ll see on every site:

Event Hook Frequency What It Does
wp_version_check Twice daily Checks WordPress.org for a newer WordPress version
wp_update_plugins Twice daily Checks all installed plugins for available updates
wp_update_themes Twice daily Checks all installed themes for available updates
wp_scheduled_delete Daily Permanently deletes posts in Trash older than 30 days
delete_expired_transients Daily Cleans up expired transients from the database
wp_privacy_delete_old_export_files Hourly Deletes old personal data export files (GDPR)
recovery_mode_clean_expired_keys Daily Removes expired recovery mode keys
publish_future_post Per scheduled post Publishes posts scheduled for a future date/time
wp_site_health_scheduled_check Weekly Runs background Site Health checks

Every plugin you install can register its own cron events on top of these. WooCommerce alone registers 6–10 additional events. A backup plugin registers its scheduled backup. An email marketing plugin registers its queue processor. On a site with 25+ active plugins, you can easily have 40–60 registered cron events.

WP-Cron vs Real Cron

WP-Cron vs Real Server Cron: When to Use Each

Feature WP-Cron (default) Real Server Cron
Runs on exact schedule βœ— Traffic-dependent βœ“ Always on time
Works on low-traffic sites βœ— Unreliable βœ“ Fully reliable
Performance impact on visitors ⚠ Adds overhead to page loads βœ“ Runs independently
Works on high-traffic sites ⚠ Can fire too frequently βœ“ Controlled frequency
Setup required βœ“ None β€” works out of the box βœ— Requires server access
Works on all hosting types βœ“ Yes ⚠ Requires cPanel / SSH access
Suitable for critical tasks βœ— Backups, emails β€” risky βœ“ Yes
Recommended for production sites βœ— No βœ“ Yes
When WP-Cron is fine WP-Cron is acceptable on high-traffic sites (where pages load constantly, so cron fires reliably) where the tasks are low-stakes β€” things like checking for plugin updates where an hour’s delay doesn’t matter. For any task that is time-sensitive β€” scheduled post publishing, backup execution, payment processing, order confirmation emails β€” replace WP-Cron with a real server cron job.
Viewing Your Cron Jobs

How to View All Scheduled WordPress Cron Jobs

There’s no way to see your scheduled cron events in the standard WordPress admin interface. You need either WP-CLI or a plugin. Here are both methods:

πŸ’» Method 1 β€” WP-CLI (Fastest, Most Complete)

If your host provides WP-CLI access (Cloudways, Kinsta, SiteGround, and most managed hosts do), this is the fastest way to see everything:

# List all scheduled cron events
wp cron event list

# List events with next run time and recurrence
wp cron event list --fields=hook,next_run_relative,recurrence

# Run a specific event manually (useful for testing)
wp cron event run wp_update_plugins

# See all registered cron schedules (hourly, daily, etc.)
wp cron schedule list

# Delete a specific cron event
wp cron event delete hook_name

The --fields flag is useful for a clean overview. On a typical WordPress site, wp cron event list outputs 15–60 rows depending on your plugin stack.

πŸ”Œ Method 2 β€” WP Crontrol Plugin (No Server Access Required)

WP Crontrol (free, 800k+ active installs) adds a Cron Events screen to your WordPress admin at Tools β†’ Cron Events. It shows every scheduled event, its next run time, its recurrence, and the file/line that registered it.

Key things you can do in WP Crontrol:

  • Run any cron event manually with one click β€” essential for testing whether an event actually works
  • Delete orphaned cron events left behind by uninstalled plugins
  • Add custom cron events without writing code
  • See exactly which plugin registered each event β€” by filename and function name
  • Edit the schedule of any existing event
On deleting cron events Don’t delete core WordPress cron events (wp_version_check, wp_update_plugins, delete_expired_transients, etc.) even if they seem redundant. Removing them can cause database bloat (from uncleared transients), missed security updates, and unexpected behaviour in plugins that depend on WordPress’s own scheduled tasks.
πŸ—“οΈ
Free WordPress Plugin Β· Coming to WordPress.org

TopTut WordPress Cron Manager

A cleaner, faster way to view, test, and fix all scheduled cron events on your WordPress site β€” directly from your dashboard. Built for developers who need more than WP Crontrol’s basic interface.

  • Visual timeline of all scheduled events with overdue highlighting
  • One-click manual trigger with execution time display
  • Orphaned event detection (events left by uninstalled plugins)
  • Stuck event alerts β€” events that are overdue by more than 2Γ— their interval
  • Export full cron schedule as CSV for client reporting
  • Built-in guide for disabling WP-Cron and setting up a real server cron
Join the Waitlist β€” Free on WordPress.org β†’
Diagnosing Failures

Why WP-Cron Is Not Working: 5-Step Diagnostic

If your scheduled posts are publishing late, your backup plugin is missing runs, or WooCommerce emails are delayed β€” WP-Cron is likely the culprit. Work through this diagnostic in order:

Is DISABLE_WP_CRON set to true in wp-config.php?
Open wp-config.php and search for DISABLE_WP_CRON. If you see define('DISABLE_WP_CRON', true);, WP-Cron has been disabled β€” intentionally or by a previous developer β€” without a real server cron job set up to replace it. Either remove that line (re-enables WP-Cron) or set up a real cron job first (see the next section below), then leave the disable flag in place.
Fix Remove the line if no server cron exists, or add a real cron job then keep the disable flag.
Can wp-cron.php be reached over HTTP?
WP-Cron works by WordPress making an HTTP request to itself. If your server blocks loopback HTTP requests β€” common on some hosting configurations and firewalls β€” WP-Cron silently fails. Test this in WordPress Admin β†’ Tools β†’ Site Health. Look for a “Loopback requests” check. If it shows a failure, your host is blocking internal HTTP requests.
Fix Contact your host to allow loopback requests, or switch to a real server cron job which bypasses this requirement entirely.
Is the site receiving enough traffic to trigger cron reliably?
Check your Google Analytics or GSC β€” if your site receives fewer than 50–100 page views per day, WP-Cron is inherently unreliable for hourly tasks. Daily tasks will likely fire, but hourly events will miss runs. For any site with moderate or low traffic, a real server cron job is the correct solution regardless of other factors.
Fix Set up a real server cron job (see next section) to guarantee reliable execution regardless of traffic.
Is the specific event registered and scheduled correctly?
Use WP Crontrol (Tools β†’ Cron Events) or wp cron event list to find the specific event. Check its next run time β€” if it shows a timestamp in the far past (days or weeks overdue), the event is stuck. This can happen when a plugin registers an event but doesn’t update the timestamp after a failed run.
Fix In WP Crontrol, delete the stuck event and re-register it β€” or deactivate and reactivate the plugin that registered it, which typically re-registers its cron events fresh.
Is a caching layer blocking wp-cron.php requests?
Some aggressive caching configurations intercept requests to wp-cron.php and serve a cached response instead of executing the file. Check your caching plugin’s exclusions list β€” wp-cron.php should be excluded from caching. In WP Rocket: File Optimization β†’ Never Cache URL β†’ add /wp-cron.php. In Cloudflare: create a Page Rule for */wp-cron.php* β†’ Cache Level: Bypass.
Fix Add wp-cron.php to your caching exclusions at both the plugin level and the CDN level.
The Proper Fix

How to Disable WP-Cron and Set Up a Real Server Cron Job

This is the correct, permanent solution for any WordPress site where reliable task scheduling matters. The setup has two parts: disable WP-Cron in WordPress, then configure your server to call wp-cron.php on a fixed schedule instead.

Step 1: Disable WP-Cron in wp-config.php

Add this line to wp-config.php, immediately above the /* That's all, stop editing! */ line:

πŸ“„ wp-config.php
/* Disable WP-Cron β€” replaced by real server cron job */
define( 'DISABLE_WP_CRON', true );

/* That's all, stop editing! Happy publishing. */

This tells WordPress to stop spawning HTTP requests to wp-cron.php on every page load. Your scheduled tasks will not run until you complete Step 2 β€” so set up the server cron job before or immediately after adding this line.

Step 2: Set Up a Real Cron Job at Your Host

The cron job needs to call wp-cron.php via PHP directly (not via HTTP). The recommended frequency is every 5 minutes β€” this ensures hourly tasks fire within their expected window and prevents any task from being delayed more than 5 minutes. Here’s how to do it on the most common hosts:

cPanel (Bluehost, HostGator, SiteGround, A2 Hosting) Most Common
  1. Log into cPanel β†’ Advanced β†’ Cron Jobs
  2. Set the frequency to Every 5 Minutes (or use the common settings dropdown)
  3. In the Command field, enter β€” replacing the path with your actual WordPress installation path:
php /home/yourusername/public_html/wp-cron.php

To find your exact path: in cPanel β†’ File Manager, navigate to your WordPress root folder and note the path in the address bar. It typically follows the pattern /home/yourusername/public_html/.

If php alone doesn’t work, try the full PHP binary path:

/usr/local/bin/php /home/yourusername/public_html/wp-cron.php
Dreamhost Panel Dreamhost-Specific
  1. Log into the Dreamhost Panel β†’ Goodies β†’ Cron Jobs
  2. Click Add New Cron Job
  3. Set the user to your shell user
  4. Set When to run: Custom β†’ enter */5 * * * * (every 5 minutes)
  5. In the Command field:
/usr/local/php82/bin/php /home/yourusername/yourdomain.com/wp-cron.php

Note: Dreamhost uses versioned PHP binary paths. Replace php82 with your active PHP version (check in the Dreamhost panel under Managed PHP).

Cloudways SSH / Crontab
  1. Connect via SSH to your Cloudways server
  2. Run crontab -e to open the crontab editor
  3. Add this line β€” replace the path with your application’s public_html path:
# WordPress cron β€” runs every 5 minutes
*/5 * * * * /usr/bin/php /home/master/applications/yourapp/public_html/wp-cron.php

Find your exact application path in the Cloudways dashboard β†’ Application β†’ Application Management β†’ Application Credentials.

Kinsta / WP Engine Managed Host

Both Kinsta and WP Engine provide a cron job management interface in their dashboards β€” you don’t need SSH access.

Kinsta: MyKinsta β†’ Sites β†’ Your Site β†’ Tools β†’ Cron Jobs β†’ Add Cron Job. Use the command:

cd /www/yoursitename/public && php wp-cron.php

WP Engine: WP Engine dashboard β†’ Sites β†’ Your Site β†’ Cron Jobs. Add a new cron job with the above command and set the frequency to every 5 minutes.

On both managed hosts, WP-Cron is often already configured or managed automatically β€” check with their support before adding a custom cron job to avoid duplicate execution.

Verify it’s working After setting up the server cron job, wait 10 minutes, then check WP Crontrol (Tools β†’ Cron Events) or run wp cron event list. The “Next Run” times should be updating β€” events that were previously overdue should now show future timestamps. If a scheduled post is due, verify it publishes within 5 minutes of its scheduled time.
Custom Cron Events

How to Register a Custom WordPress Cron Job in Code

If you’re building a plugin or custom functionality that needs to run on a schedule, here’s the correct way to register a cron event in WordPress. There are two parts: scheduling the event, and hooking your function to it.

πŸ“ Registering a Custom Cron Event β€” functions.php or Plugin File
/**
 * Schedule a custom cron event on plugin/theme activation.
 * This registers the event only once β€” wp_next_scheduled prevents
 * duplicate registrations on subsequent page loads.
 */
function toptut_schedule_my_task() {
    if ( ! wp_next_scheduled( 'toptut_my_custom_event' ) ) {
        wp_schedule_event(
            time(),                  // Start time (Unix timestamp)
            'hourly',               // Recurrence: hourly, twicedaily, daily, weekly
            'toptut_my_custom_event' // Hook name β€” must be unique
        );
    }
}
add_action( 'wp', 'toptut_schedule_my_task' );


/**
 * Hook your actual function to the cron event.
 * This is what runs when the scheduled event fires.
 */
function toptut_my_task_callback() {
    // Your code here β€” e.g. send an email, process a queue, clean a table
    error_log( 'toptut_my_custom_event fired at: ' . current_time( 'mysql' ) );
}
add_action( 'toptut_my_custom_event', 'toptut_my_task_callback' );


/**
 * Clean up on plugin/theme deactivation.
 * Always remove your cron events when your plugin is deactivated β€”
 * orphaned cron events accumulate in the database otherwise.
 */
function toptut_deactivate_my_task() {
    $timestamp = wp_next_scheduled( 'toptut_my_custom_event' );
    wp_unschedule_event( $timestamp, 'toptut_my_custom_event' );
}
register_deactivation_hook( __FILE__, 'toptut_deactivate_my_task' );

The three most common mistakes when registering custom cron events:

  • Missing the wp_next_scheduled check β€” without it, every page load registers a new copy of the event, leading to hundreds of duplicate entries in the cron table
  • Not cleaning up on deactivation β€” orphaned events from uninstalled plugins accumulate and waste execution time on every cron run
  • Using a non-unique hook name β€” if two plugins register events with the same hook name, they will interfere with each other

Adding a Custom Cron Schedule (Custom Interval)

WordPress’s built-in schedules are hourly, twicedaily, daily, and weekly. If you need a different interval β€” every 5 minutes, every 30 minutes, every 6 hours β€” register a custom schedule:

πŸ• Registering a Custom Cron Interval
function toptut_add_cron_intervals( $schedules ) {

    // Every 5 minutes
    $schedules['every_five_minutes'] = array(
        'interval' => 5 * 60,
        'display'  => __( 'Every 5 Minutes' ),
    );

    // Every 30 minutes
    $schedules['every_thirty_minutes'] = array(
        'interval' => 30 * 60,
        'display'  => __( 'Every 30 Minutes' ),
    );

    return $schedules;
}
add_filter( 'cron_schedules', 'toptut_add_cron_intervals' );

Once registered, the custom schedule name (every_five_minutes) can be used in any wp_schedule_event() call as the recurrence parameter.

WP-CLI Reference

WP-CLI Cron Commands: Complete Reference

Command What It Does
wp cron event list Lists all scheduled cron events with next run time and recurrence
wp cron event run {hook} Manually runs a specific cron event immediately β€” useful for testing
wp cron event delete {hook} Removes a scheduled event (next occurrence only)
wp cron event schedule {hook} {time} {recurrence} Schedules a new cron event from the command line
wp cron schedule list Lists all registered cron schedule intervals (hourly, daily, custom)
wp cron test Tests whether WP-Cron is working on your site β€” returns a pass/fail with details
wp option get cron Outputs the raw cron option from the database β€” the actual data structure WordPress uses
wp cron event list –due-now Shows only events that are currently due to run β€” useful for debugging missed events
Quick test for a working cron setup Run wp cron test after setting up your server cron job. It will return a pass/fail and show the last time cron ran. If it returns a failure even after your server cron is set up, the issue is usually the path to wp-cron.php in your crontab command β€” double-check it matches the actual file system path of your WordPress installation.

See All Your WordPress Cron Jobs at a Glance

The TopTut Cron Manager plugin shows every scheduled event, flags overdue and stuck tasks, and walks you through replacing WP-Cron with a real server cron job β€” without touching the command line.

Get the Free Plugin β†’
FAQ

Frequently Asked Questions

What is a WordPress cron job?
A WordPress cron job is a scheduled background task that runs automatically β€” things like publishing scheduled posts, sending emails, checking for plugin updates, and clearing expired database entries. WordPress implements cron through a system called WP-Cron, which triggers on page visits rather than on a fixed time schedule. This means WP-Cron is unreliable on low-traffic sites β€” tasks run late or not at all if no one visits the site when the task is due.
Why is my WP-Cron not working?
The most common causes are: DISABLE_WP_CRON is set to true in wp-config.php without a real cron job replacing it, the server is blocking loopback HTTP requests (check Tools β†’ Site Health), the site has too little traffic to trigger cron reliably, or a caching plugin is intercepting requests to wp-cron.php. Work through the five-step diagnostic in this guide, starting with checking wp-config.php for the disable flag.
How do I disable WP-Cron in WordPress?
Add define('DISABLE_WP_CRON', true); to your wp-config.php file, above the line that says /* That's all, stop editing! */. This prevents WordPress from spawning HTTP requests to wp-cron.php on every page load. Important: once you disable WP-Cron, your scheduled tasks will stop running until you set up a real server-side cron job to replace it. Set up the server cron job before or immediately after disabling WP-Cron.
How often should I run the WordPress cron job?
Every 5 minutes is the standard recommendation for a real server cron job replacing WP-Cron. This frequency ensures that hourly WordPress tasks fire within their expected window, that scheduled posts publish within 5 minutes of their scheduled time, and that no task is delayed long enough to cause visible problems. Running more frequently than every 5 minutes provides diminishing returns for most WordPress sites. If you have very time-sensitive tasks (such as payment processing), consider every 1–2 minutes.
How do I view scheduled cron jobs in WordPress?
WordPress doesn’t show scheduled cron events in its standard admin interface. To view them, either install the WP Crontrol plugin (free, adds a Cron Events screen at Tools β†’ Cron Events) or use WP-CLI with the command wp cron event list. Both show every registered event, its next run time, and its recurrence interval. WP-CLI additionally lets you run events manually with wp cron event run {hook}.
Can WordPress cron jobs affect site performance?
Yes, in two different ways. On low-traffic sites, WP-Cron causes tasks to pile up and then all run simultaneously when the next visitor arrives β€” creating a sudden performance spike. On high-traffic sites, WP-Cron fires on every page load, adding small but measurable overhead to each request. Disabling WP-Cron and replacing it with a real server cron job eliminates both problems: tasks run on schedule without adding overhead to visitor page loads.
Previous Article

How to Auto-Categorize WordPress Posts using LLM APIs

Next Article

The True Cost of WordPress Autoblogging in 2026

Liza Kliko
Author:

Liza Kliko

I have been in online business before Facebook, Instagram, and Twitter ever existed. I was making money online before it was cool. Today, I share my experience and knowledge with my readers.

Index