When WordPress Cron Goes Silent: Diagnosing and Fixing WP-Cron Failures on Managed Hosts

You schedule a post. The deadline passes. Nothing. You check the queue, and it’s just sitting there, stuck in “Scheduled” like a car with no engine. The problem isn’t your theme, a plugin conflict, or a missed click in the editorial calendar. The host killed your cron. Quietly. They flipped a switch that stops WordPress from running its own task scheduler, and they didn’t bother to tell you. For small-to-mid publishing teams, this is the kind of silent failure that turns a routine Tuesday into a frantic scramble.

WordPress cron isn’t a real cron daemon. It’s a virtual scheduler that wakes up only when someone visits your site. If traffic is low, cron sleeps. If the host blocks the site from calling itself, cron sleeps. If a server-level rule neuters wp-cron.php, cron sleeps. And when it sleeps, your scheduled posts, backup routines, and editorial notification plugins all sleep with it. The fix isn’t a plugin. It’s a hard cut over to the server’s actual cron system, and a clear understanding of why the virtual one failed in the first place.

Server rack with blinking lights, representing the hidden infrastructure where cron failures originate
Server-level cron failures often originate in configurations invisible to the WordPress dashboard.

The Architecture of WordPress Cron and Why It Fails

WordPress cron is not a background process. It’s a trigger embedded in the front-end request lifecycle. Every time a page loads, WordPress checks a queue of scheduled events stored in the wp_options table under the cron option. If an event’s scheduled time has passed, WordPress tries to execute it by sending a loopback HTTP request to /wp-cron.php. This request is non-blocking by default, using wp_remote_post() with a timeout of 0.01 seconds. The idea is to fire the event without slowing the page load. The reality is that this mechanism is fragile and breaks in predictable ways.

How Managed Hosts Disable WP-Cron Without Telling You

Many managed WordPress hosts disable the default cron behavior by adding a line to wp-config.php:

define('DISABLE_WP_CRON', true);

This constant stops WordPress from spawning the loopback request on page loads. The host’s intention is to replace it with a real server-side cron job that hits wp-cron.php directly. But if that server-side cron is misconfigured, missing, or silently removed during a platform update, your scheduled tasks stop. No error is logged in the WordPress dashboard. The only symptom is that future-dated posts stay in “Scheduled” status, and plugin tasks like backup rotations or editorial digest emails never fire.

Another common failure mode is loopback blocking. Some security configurations, such as ModSecurity rules or restrictive .htaccess files, block the server from making HTTP requests to itself. The result is identical: the cron request is never received, and the event queue stalls. You can test this directly from the command line with curl -I https://yoursite.com/wp-cron.php. If the response is anything other than HTTP 200, you have a loopback problem.

How to Detect a Silent Cron Failure

Don’t rely on the WordPress Site Health tool alone. It can report that “WP-Cron is working” even when it’s not. Instead, use a direct test:

  1. Create a post and schedule it for two minutes in the future.
  2. Open a new incognito browser window and visit the site’s homepage. Refresh several times over the next five minutes.
  3. Check the post status. If it remains “Scheduled,” cron is not executing.

For a more precise diagnosis, query the cron option directly. Run this SQL:

SELECT option_value FROM wp_options WHERE option_name = 'cron';

Unserialize the result. Look for timestamps older than the current server time. If you see events with past timestamps that never cleared, the queue is stalled. This is not a plugin conflict. This is a delivery failure.

Close-up of a server motherboard, emphasizing the hardware-level decisions that affect cron execution
Diagnosing cron failures requires looking past the application layer and into server-level configurations.

The Exact Fix: Replacing WP-Cron with a System Cron Job

The only reliable solution is to bypass WordPress’s virtual cron entirely and use the server’s real cron daemon. This eliminates dependency on site traffic and loopback requests. The steps are specific and must be followed in order.

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

Add this line to your wp-config.php file, above the “That’s all, stop editing” comment:

define('DISABLE_WP_CRON', true);

This stops WordPress from attempting to spawn cron on page loads. It does not delete the event queue. It simply prevents the broken delivery mechanism from firing.

Step 2: Create a System Cron Job That Calls wp-cron.php Directly

Access your server’s crontab. For most Linux-based hosts, use:

crontab -e

Add a line that executes wp-cron.php via the PHP command-line interface (CLI) or via wget. The CLI method is preferred because it avoids HTTP entirely and is not subject to loopback restrictions or execution time limits. The command is:

*/15 * * * * /usr/bin/php /path/to/your/site/wp-cron.php > /dev/null 2>&1

Replace /usr/bin/php with the correct path to your PHP binary. You can find it with which php. Replace /path/to/your/site with the absolute server path to your WordPress installation. The > /dev/null 2>&1 part discards output so you don’t get an email every 15 minutes.

If your host restricts CLI access, use wget as a fallback:

*/15 * * * * wget -q -O - https://yoursite.com/wp-cron.php > /dev/null 2>&1

This still relies on a loopback HTTP request, but it is initiated by the server’s cron daemon, not by a page load. It is more reliable than the default mechanism, but it can still fail if the host blocks outbound HTTP from cron or if DNS resolution is broken internally. Test it immediately after adding.

Step 3: Verify Execution and Clear the Stalled Queue

After setting the system cron, wait 15 minutes, then check the cron option again. Past-due events should now be cleared. If they are not, manually trigger cron from the command line to see errors:

/usr/bin/php /path/to/your/site/wp-cron.php

Watch for PHP fatal errors, memory exhaustion, or plugin-specific failures. A common issue is that a plugin registered a cron callback that relies on the HTTP context (e.g., it uses wp_remote_get() internally and the server blocks outbound requests). In that case, you must fix the plugin or replace it. The cron system itself is now working; the failure is in the callback.

If you see a “Nothing Found” error when trying to view scheduled posts after fixing cron, the issue may be related to permalink flushing or query alterations caused by the stalled queue. See What to Fix First When a New WordPress Site Says Nothing Found for a methodical approach to that specific symptom.

Why Plugins Like WP Crontrol Are Not the Answer

Plugins such as WP Crontrol let you view and manually run cron events. They are excellent debugging tools. They do not fix a broken delivery mechanism. If DISABLE_WP_CRON is set to true, or if loopback requests are blocked, clicking “Run Now” in WP Crontrol will do nothing. The plugin still relies on the same broken pathway. Use WP Crontrol to inspect the queue and identify stalled events, but do not mistake its interface for a solution.

When the Host Refuses to Give You Crontab Access

Some managed hosts lock down crontab and do not allow custom cron jobs. In this scenario, you have two options. First, contact support and demand they verify the server-side cron is correctly configured and actually running. Provide them with the exact test: schedule a post, wait, and show them the stalled cron option. Second, if they cannot or will not fix it, use an external cron monitoring service like EasyCron or Cron-job.org to ping wp-cron.php on a schedule. This is a last resort because it introduces an external dependency, but it is better than a silent failure.

Network cables connected to a server, illustrating the external dependencies that can be used as a cron fallback
External cron services can act as a fallback when the host refuses to provide direct crontab access.

Preventing Recurrence: Monitoring and Documentation for Teams

For a publishing team, a cron failure is a process failure. The fix is not just technical; it is operational. Implement a lightweight monitoring check that runs daily and confirms the cron queue is not stalled. A simple WP-CLI command can be scripted:

wp cron event list --fields=hook,next_run_relative --format=csv

Pipe this into a log file and alert if any event’s next run is more than one hour in the past. This can be integrated into existing server monitoring or run as a separate health-check script.

Document the cron configuration in your team’s runbook. Include the exact crontab entry, the PHP binary path, and the contact information for host support. When a new team member joins, or when the site is migrated, this documentation prevents the same failure from recurring. Assume the host will disable cron again during a platform update. It has happened before. It will happen again.

FAQ

Why does WordPress use a virtual cron instead of a real one?

WordPress uses a virtual cron to avoid requiring server-level access for basic scheduling. This design allows the software to run on cheap shared hosting where crontab is not available. The tradeoff is reliability: the virtual cron depends on site traffic and loopback requests, both of which can fail silently.

How can I tell if my host disabled WP-Cron without warning?

Check your wp-config.php file for the line define('DISABLE_WP_CRON', true);. If it exists and you did not add it, the host added it. Then verify whether a corresponding server-side cron job exists. If it does not, or if it is misconfigured, your scheduled tasks are not running. The definitive test is to schedule a post and observe whether it publishes without manual intervention.

Can I use a plugin to fix a broken cron system?

No. Plugins like WP Crontrol can help you view and debug the cron queue, but they cannot execute events if the underlying delivery mechanism is broken. If DISABLE_WP_CRON is set to true or loopback requests are blocked, no plugin can override that. The fix must happen at the server level with a real cron job or an external ping service.

What is the safest interval for a system cron job?

Fifteen minutes is the standard interval and works for most publishing workflows. Setting it lower (e.g., every 5 minutes) can increase server load unnecessarily, especially on sites with many cron events. Setting it higher (e.g., every 30 minutes) may delay time-sensitive tasks like scheduled posts. Test with 15 minutes and adjust only if you have a specific, measured need.