If you've ever noticed that a WordPress scheduled event — a plugin's cleanup task, a scheduled post, an automated backup — stopped running without any error or warning, you've hit one of WordPress's least-understood quirks: WP-Cron.
Unlike a real cron job running on the server, WP-Cron is triggered by HTTP requests to your site. No visitors = no scheduled jobs. Low-traffic sites can miss hours of scheduled work. High-traffic sites can see the same job triggered multiple times simultaneously. Neither is what you want.
Every time a page loads on a WordPress site, WordPress checks a list of pending scheduled events. If an event is overdue, it fires it — in the same HTTP request, before the page renders. The code lives in wp-includes/cron.php and is triggered by the wp_loaded action on every front-end and admin request.
This design has some obvious failure modes:
The easiest way to see what's in your cron queue is the WP Crontrol plugin (free on WordPress.org). It shows all scheduled events, their next run time, and whether they're overdue. An event that's listed as "overdue by 4 hours" on a healthy site indicates a problem.
From the command line with WP-CLI:
wp cron event list
To manually trigger all overdue events:
wp cron event run --due-now
WP-Cron spawns a loopback HTTP request to yoursite.com/wp-cron.php. If your server can't reach itself (common in some hosting environments, behind certain firewalls, or with misconfigured SSL), scheduled events will queue up indefinitely.
Test the loopback with WP-CLI:
wp eval 'echo wp_remote_get(site_url("wp-cron.php"))["response"]["code"];'
If the response is not 200, you have a loopback problem.
Some performance guides and managed hosting providers recommend adding this to wp-config.php:
define('DISABLE_WP_CRON', true);
This disables the built-in WP-Cron trigger. If it's set and you haven't configured a real server cron to call wp-cron.php, all your scheduled events will stop running permanently — and nothing will warn you.
For any site that matters, the right fix is to disable WP-Cron's default behavior and replace it with a real server-side cron job:
Add this to wp-config.php:
define('DISABLE_WP_CRON', true);
Add this to your server's crontab (run crontab -e):
# Run WordPress cron every 5 minutes
*/5 * * * * wget -q -O - https://yoursite.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1
Or with curl:
*/5 * * * * curl -s https://yoursite.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1
Or with WP-CLI (more reliable, handles WordPress-internal locking correctly):
*/5 * * * * cd /path/to/wordpress && wp cron event run --due-now --quiet
On managed hosts like WP Engine, Kinsta, or Flywheel, check their documentation — they typically provide a built-in cron replacement and may require you to use their UI rather than direct crontab access.
After setting up the real cron, confirm it runs every 5 minutes. Create a temporary scheduled event in WordPress and watch with WP Crontrol to verify it fires on time. Or watch cron logs:
grep CRON /var/log/syslog | tail -20
Even after the fix, your server cron can silently fail. The /5 * * * * entry in crontab can break if:
curl starts failing silentlyThe pattern you want is a dead man's switch: your cron job actively checks in on a schedule, and you get alerted if check-ins stop.
Add a ping to a monitoring service after your WordPress cron runs successfully. For example, with a service like CronWatch:
*/5 * * * * curl -s https://yoursite.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1 \
&& curl -s https://your-monitor.example.com/heartbeat/your-token > /dev/null 2>&1
If the heartbeat stops arriving for more than your grace period, you get alerted immediately — not when a user notices that scheduled posts stopped publishing or a plugin stopped sending emails.
| Plugin | What it does | Cost |
|---|---|---|
| WP Crontrol | Inspect + manually run events | Free |
| Action Scheduler | Reliable async job queue (used by WooCommerce) | Free |
| WP Cron Status Checker | Alerts when cron hasn't run recently | Free |
| Cron Job Monitoring (generic) | Dead man's switch monitoring with alerts | Varies |
These are the scheduled events that site owners most commonly discover have been silently broken for days or weeks:
publish_future_post never fires on low-traffic or misconfigured siteswoocommerce_cleanup_orders leaves failed/pending orders accumulatingwp eval 'echo wp_remote_get(site_url("wp-cron.php"))["response"]["code"];' — expect 200DISABLE_WP_CRON status in wp-config.phpDISABLE_WP_CRON = true, verify the replacement server cron is runningCronWatch is a dead man's switch monitor for cron jobs. Add one line to your cron command, and we alert you if a job is late or stopped checking in — before your users notice something is wrong.
Works with WordPress cron, standard crontab, GitHub Actions, AWS EventBridge, Kubernetes CronJobs, and any scheduled process.
Get early access → Free cron parser