Troubleshooting guide · wordpress · Published 2026-08-16 · 4 min read

WordPress wp-cron troubleshooting

WordPress wp-cron troubleshooting: how scheduled jobs fire, the causes of stuck cron, and how to switch to a real scheduler.

How WordPress scheduling actually works

WordPress does not run a background daemon like a server cron. Instead, on most sites it relies on wp-cron.php, which fires when a visitor loads a page: the request is checked for due events, and if any are due the site makes an internal request to wp-cron.php to run them. That is the behaviour-based cron: no visits means no cron, and a very quiet site can miss scheduled posts, scheduled image compression, plugin license checks, and the automatic update checks.

The failure mode is rarely "the site does not understand the schedule". It is almost always one of:

  1. No traffic triggers nothing at all.
  2. DISABLE_WP_CRON is set to true, which turns the visitor-triggered check off entirely, and no external cron feed was configured to replace it.
  3. wp-cron.php is blocked: an IP-denial rule, a 404 rewrite battle, the CDN caching it (Cloudflare serving a cached empty document), or the server firewalling loopback requests.
  4. A hung event with a stuck row in the cron option array blocks all later events.

The WordPress troubleshooting order is the right lens: figure out whether the scheduler, the event, or the request to wp-cron.php failed, not just that "the post did not publish".

Diagnose: the two checks

Check 1: is cron enabled but failing?

Read the last cron run via wp-cli if available:

wp cron event list --fields=hook,next_run_relative,status

A list with all events showing 0 next-run time, or a cluster of stuck "past-due but not run" entries, means the cron has not been triggered or is stuck.

Check 2: does direct hit to wp-cron.php work?

curl -I https://example.com/wp-cron.php

Expect a 200 with an empty body. A 404, 403, or a cached HTML page is the smoking gun. If the response is wrong, see the DNS and SSL and error pages pages for where the block lives; check the server error log path from the error log article.

The two common fixes

Enable the real cron and disable the visitor trigger.

If the host allows a crontab (most shared hosts give you Cron Jobs in cPanel), set the visitor check off and point a real scheduler at the check endpoint:

define( 'DISABLE_WP_CRON', true );

Then in cron (cPanel > Cron Jobs, or a host scheduled task), add a job that wakes WordPress at least every few minutes and processes due events. A reasonably reliable line:

curl https://example.com/wp-cron.php

or using PHP CLI when the CLI is available (hits the file directly without a web request):

php /path/to/wordpress/wp-cron.php

Expect delayed, not broken: WordPress cron is best-effort on passive timing. Jobs run on the next page load after they are due, and the wp-cron.php spawn is throttled to roughly once per minute on a busy site (the doing_cron transient). So a scheduled post set for 14:00 on a quiet site publishes on the next visit after 14:00, not at 14:00 exactly. If events consistently fire 10 minutes late on a site with normal traffic, look at the throttling and the stuck-row check above, not at a "bug" in the schedule itself.

When wp-cron is the wrong suspect

Some scheduler problems look like cron but are not:

Prevention

Run the actual wp-cron on a real timer from the start, keep DISABLE_WP_CRON false while you develop (or a real external schedule wins), and watch the "Scheduled (Cron) Events" row in Tools > Site Health > Info > Scheduled Actions for missed count after plugin changes. A site with a real cron has no "hidden visitor tax" and no stuck row surprises during updates.

Need a website built, fixed, optimised, migrated or replaced?

This technical resource is written by CSMBAC, a small design and development studio. If you would rather hand the problem to a professional, the website service page explains how we build enquiry-ready websites.

Explore website services