Skip to content
ThemesIonic — home
WordPress Tutorials

WordPress Cron

WP-Cron is not a scheduler. It is a queue checked when somebody visits your site, which explains every scheduled post that never published and every backup that silently stopped running.

4 min read intermediate

WP-Cron runs when somebody loads a page, not when the clock says so. WordPress keeps a list of due tasks in the database, and on each request it checks whether anything is overdue and fires it. There is no timer, no daemon and no guarantee.

Every strange scheduling behaviour on a WordPress site follows from that one sentence.

What that means in practice

  • A quiet site runs nothing. No visitors between midnight and eight means nothing scheduled in that window happens until the first morning request.
  • A busy site checks constantly, adding a small amount of work to a large number of requests.
  • Tasks run late, never early. "Hourly" means "at least an hour, then whenever someone next arrives".
  • A fully page-cached site may not trigger it, because cached responses can be served without loading WordPress at all.

That last one surprises people: making a site faster can stop its scheduled tasks, because the requests that used to trigger the queue now never reach PHP.

What depends on it

More than most people realise:

Task Owner
Publishing scheduled posts Core
Checking for core, plugin and theme updates Core
Sending pending email queues Plugins
Running backups Backup plugins
Rebuilding search or filter indexes Search and filter plugins
Abandoned cart and follow-up sequences Ecommerce plugins
Deleting expired transients and logs Core and plugins

When WP-Cron is unreliable, all of these are unreliable together — which is why "my backups stopped" and "my scheduled posts miss" are usually one problem with one fix.

The loopback requirement

WP-Cron works by making WordPress request itself over HTTP. If that request cannot complete, the queue never processes even on a busy site.

Loopbacks fail for mundane reasons: a security plugin blocking self-requests, HTTP authentication on a staging site, a hosts-file mismatch, a firewall, or a certificate the server does not trust for its own domain. The site health screen reports it, and loopback request failed covers the diagnosis in full. Fixing the loopback fixes cron without any other change.

The proper arrangement

On any site where timing matters, stop using visitor-triggered cron and run the queue from the server.

Step one — disable the built-in trigger in wp-config.php:

define( 'DISABLE_WP_CRON', true );

Step two — schedule a real cron job to process the queue every few minutes. With WP-CLI, which is the cleaner route:

*/5 * * * * cd /var/www/example.com && wp cron event run --due-now >/dev/null 2>&1

Or, if WP-CLI is unavailable, request wp-cron.php directly:

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

Most hosting panels offer a cron interface for this, and managed hosts often do it for you — check before adding a second one. Disabling WP-Cron covers the constant and its consequences, and the WP-CLI route is in WP-CLI commands.

Do not do step one without step two. Disabling the trigger with nothing replacing it stops every scheduled task on the site silently, which is a worse position than an unreliable cron.

Seeing what is actually scheduled

Sites accumulate scheduled events, including from plugins deleted years ago. The queue is visible:

wp cron event list

What to look for: events with no matching plugin installed, events scheduled every minute, and duplicates registered several times. A queue with hundreds of entries makes every check slower and is worth cleaning — the general performance context is in speeding up WordPress and wp-admin speed.

The stack view of where this work runs — PHP processes, workers, and why a cron run competes with visitors for them — is in the server stack.

Long tasks and the timeout problem

WP-Cron runs inside a normal PHP request, so it is bound by the same execution time limit. A task that takes longer than the limit is killed part-way, often leaving whatever it was doing half done.

For anything heavy — a large import, a full backup, a bulk regeneration — the right pattern is batching: process a chunk, reschedule immediately, repeat. Plugins that do this well are the ones whose imports survive on shared hosting. Plugins that do not are why an import "stops at 60%".

Common mistakes

  • Disabling WP-Cron with no replacement.
  • Two schedulers, a host cron and the built-in trigger both running, doubling every task.
  • A five-second cron interval, which is load, not reliability.
  • Blaming the plugin when the actual fault is a blocked loopback.
  • Assuming a scheduled post published without checking, on a site with no overnight traffic.

Verify

Schedule a post two minutes ahead on a quiet site and leave the browser closed; if it publishes on time, cron is running independently of visitors. Then run wp cron event list and confirm the next run times move as expected. Finally check site health for loopback errors — a green loopback and a server cron together are the only combination that makes scheduling on WordPress genuinely dependable. If something has already failed, debug mode will show what the task threw.

Frequently asked

No. It is a list of due tasks stored in the database and checked when WordPress loads a page. With no traffic nothing runs, which is why a quiet site's scheduled tasks stop happening and a busy site's happen too often.
Almost always because nothing triggered WP-Cron at the scheduled time — no visitor, or loopback requests are blocked. The post sits in a missed-schedule state until the next page load processes the queue.
On any site where timing matters, yes — disable the visitor-triggered version and run the same queue from a real server cron every few minutes. That makes tasks reliable and removes the work from visitor page loads.

Related guides