# WP-Cron Not Working: Moving Tasks to System Cron

> WP-Cron fails to fire on a low-traffic site — disable the built-in pseudo-cron and move WordPress tasks to the server's system cron.

WP-Cron in WordPress does not run on an operating-system schedule; it fires when a visitor loads the site: the `wp-cron.php` file is pinged on every page load and checks whether a scheduled task is due. On a site with rare visits, scheduled publishing and mailings run hours late. Here is how to fix that with the system cron.

## Why WP-Cron is late or does not fire at all

The WordPress pseudo-cron lives only while there is traffic: no visit means no task check. A second cause of failures is shared hosting with aggressive page caching: a caching plugin serves a stored HTML copy and never lets the request reach PHP, and therefore never reaches `wp-cron.php`. A third cause is an outbound self-request blocked at the server level, which stops WordPress from pinging itself over HTTP.

## How to check the current state of tasks

The list of scheduled tasks and their exact time is easy to check through WP-CLI, if it is available on the hosting.

```
wp cron event list --fields=hook,next_run_relative,recurrence
```

If the `next_run_relative` column shows a negative value like "-3 hours", the task is overdue and is waiting for the next visitor to arrive so it can run.

## How to disable the built-in pseudo-cron

Disabling it takes one constant in `wp-config.php` — add the line before the "That's all, stop editing!" marker.

```
<?php
define('DISABLE_WP_CRON', true);
```

After this change, WordPress stops checking tasks on every visit, but the tasks themselves stay in the database — their run simply waits for an outside trigger.

## How to set up system cron instead of the pseudo-cron

The task must be handed to the operating system's own scheduler. Add a line through `crontab -e` under the user account the site runs as.

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

Running every 5 minutes is a reasonable compromise for most sites: mailings and scheduled posts do not run noticeably late, and server load stays low. If WP-CLI is available on the server, calling it directly is more reliable — it skips the HTTP layer and does not depend on page cache.

```
*/5 * * * * cd /var/www/site.example/public_html && wp cron event run --due-now --quiet
```

## How to choose an interval for different tasks

| Task | Recommended interval | Effect of being late |
| --- | --- | --- |
| Scheduled post publishing | 1-5 minutes | content goes live later than planned |
| Scheduled email mailing | 5 minutes | emails go out in a delayed batch |
| Plugin temp data cleanup | 15-30 minutes | database size grows |
| Core and plugin update checks | 1 time per hour | update notice is delayed |

## Common mistakes when moving to system cron

A forgotten `DISABLE_WP_CRON` constant causes duplication: tasks run both on the system schedule and on the old pseudo-cron during a visit, so mailing emails can go out twice. A second mistake is having cron set up on the server while the site still runs [LiteSpeed Cache](https://www.zevshost.net/en/kb/wordpress-litespeed-cache/) or a similar plugin that caches `wp-cron.php` itself as a plain page — exclude this path from the cache rules explicitly. A full list of steps for a domain change and the related cron migration tasks is in the article about [changing a WordPress domain](https://www.zevshost.net/en/kb/wordpress-smena-domena-url/).

Checking that the move worked is simple: an hour after setting up system cron, run `wp cron event list` again and confirm the `next_run_relative` column keeps moving forward even without site visits. Other commands are useful for managing and diagnosing tasks too — a set of them is collected in the article about [WP-CLI](https://www.zevshost.net/en/kb/wp-cli-commands/).

## Summary

- WP-Cron by default fires only on a visitor's request, so tasks run late on quiet sites.
- The pseudo-cron can be disabled with the `DISABLE_WP_CRON` constant in `wp-config.php`.
- The task must be handed to the operating system's crontab with a 5-minute interval via curl or directly through WP-CLI.
- After the move, check that tasks are not running twice because of a forgotten constant.
- Caching plugins should be configured so they never serve `wp-cron.php` from cache.

---

Source: https://www.zevshost.net/en/kb/wordpress-cron-sistemnyy/
