How to Handle WordPress Cron When Your Host Disables Real Cron Without Warning

WordPress cron is not a real cron daemon. It is a scheduled task system that runs only when someone visits your site. If your host disables real cron without warning, you lose the ability to run scheduled jobs reliably. This article covers the exact failure modes, how to detect the problem, and the specific fixes that work on shared hosting, managed WordPress platforms, and VPS setups. It is written for small-to-mid publishing teams that depend on editorial workflow automation, scheduled imports, and content expiration jobs.

Adjacent concepts include WP-Cron, system cron, loopback requests, DISABLE_WP_CRON, wp-cron.php, and server-level cron jobs. The core issue is simple: WordPress schedules events in the wp_options table, but the execution trigger is a web request. When that trigger is removed or blocked, scheduled publishing, backup jobs, and cleanup tasks silently stop. This is a systems engineering problem, not a plugin problem.

Why WordPress Cron Fails Silently

WordPress cron is a pull-based system. On every page load, WordPress checks the cron option for due events. If an event is due, WordPress sends a loopback HTTP request to wp-cron.php. That request runs the scheduled callback. The loopback request is asynchronous, but it still depends on the web server accepting a connection to itself.

Hosts disable real cron in several ways. Some block loopback requests at the firewall. Some set DISABLE_WP_CRON to true in wp-config.php without telling you. Some remove the wp-cron.php file or block access to it via .htaccess. Managed WordPress hosts often replace WP-Cron with their own system cron, but the transition can leave orphaned events or duplicate runs.

The first symptom is usually a missed scheduled post. A writer sets a post to publish at 9:00 AM. At 9:05 AM, the post is still in draft. The editor checks the post status, sees no error, and assumes the writer forgot to schedule it. The real cause is that no visitor triggered the cron job, or the loopback request was blocked.

Detecting a Disabled Cron

Do not guess. Check the actual state of the cron system. Install the WP Crontrol plugin or run a direct database query. The wp_options table contains a row with option_name = 'cron'. The option_value is a serialized array of scheduled events. If the array is empty, WordPress has no scheduled events. If the array contains events with past timestamps, the events are overdue and not running.

Check the DISABLE_WP_CRON constant. Open wp-config.php and search for define('DISABLE_WP_CRON', true);. If it is present, WordPress will not run cron on page loads. That is the most common silent killer. Some hosts add this line during a migration or security hardening pass without notifying the site owner.

Check the loopback request. Run this from the WordPress admin area: go to Tools → Site Health → Info → Server. Look for the “Loopback request” test. If it fails, the server cannot make an HTTP request to itself. That means WP-Cron cannot run even if DISABLE_WP_CRON is not set.

The Exact Fix: Replace WP-Cron with System Cron

The reliable fix is to disable WP-Cron and run wp-cron.php from a real system cron job. This removes the dependency on page views and loopback requests. It also gives you a predictable execution interval.

Step one: add this line to wp-config.php, above the “That’s all, stop editing!” comment:

define('DISABLE_WP_CRON', true);

Step two: create a system cron job that calls wp-cron.php directly. On a typical cPanel host, open the Cron Jobs tool and add a new job. The command should be:

wget -q -O - https://yourdomain.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1

Or use PHP CLI if available:

php /home/username/public_html/wp-cron.php

Set the interval to every 5 or 10 minutes. Do not set it to every minute. That creates unnecessary load and can cause overlapping runs if a job takes longer than the interval.

Step three: verify the job runs. Check the server’s cron log or add a temporary logging line to a plugin. The simplest verification is to schedule a test post 10 minutes in the future, then wait. If the post publishes, the system cron is working.

Handling Managed WordPress Hosts

Managed hosts like Kinsta, WP Engine, and Flywheel disable WP-Cron by default and run their own system cron. You cannot add a server cron job on these platforms. Instead, you must use their built-in cron replacement. Kinsta runs a system cron every minute and triggers WP-Cron if events are due. WP Engine runs a similar system. If scheduled events are not running on a managed host, the problem is usually a plugin conflict or a long-running job that times out.

Check the host’s documentation for cron behavior. Do not assume that DISABLE_WP_CRON is the problem. On managed hosts, the constant is often set by the host and should not be removed. Instead, look for plugin-level issues: a plugin that calls wp_clear_scheduled_hook() on every load, a plugin that schedules events with a past timestamp, or a plugin that throws a fatal error during cron execution.

Debugging Overdue Events

When events are overdue, the first step is to list them. WP Crontrol shows the event name, the scheduled time, and the callback function. Look for events with a timestamp in the past. Those are stuck. The cause is usually one of three things:

  • The event’s callback function no longer exists. A plugin was deactivated or deleted, but its scheduled events remain in the database.
  • The event’s callback function throws a fatal error. WordPress catches the error and marks the event as failed, but the event remains in the queue.
  • The event is scheduled with a past timestamp because a plugin used wp_schedule_single_event() with a wrong timezone or a negative offset.

To fix a stuck event, delete it and reschedule it. In WP Crontrol, click “Delete” next to the event. Then trigger the plugin’s scheduling function again, or manually add the event with the correct timestamp. Do not edit the serialized array in the database directly. That is a fast way to corrupt the cron option and break every scheduled event on the site.

Timezone and Offset Bugs

WordPress stores cron timestamps in Unix time, which is timezone-independent. But plugins often calculate the timestamp using current_time('timestamp') or time() plus an offset. If the site’s timezone setting in Settings → General is wrong, the offset is wrong. A plugin that schedules an event for “tomorrow at 9:00 AM” may actually schedule it for 9:00 AM UTC, which is 2:00 AM in New York. The event runs, but at the wrong time.

Check the site’s timezone setting. Then check the plugin’s scheduling code. If the plugin uses strtotime() with a date string, the timezone is the server’s default timezone, not WordPress’s timezone. That mismatch causes events to fire hours early or late. The fix is to use wp_date() or DateTime with the WordPress timezone explicitly set.

Preventing Silent Cron Failures

Do not rely on page views to run scheduled jobs. Even on a busy site, traffic is not evenly distributed. A publishing team that works 9-to-5 will see traffic spikes during the day and silence at night. A scheduled post set for 6:00 AM may not publish until 8:30 AM when the first visitor arrives. That is not a failure of the cron system; it is a failure of the trigger model.

Set up a monitoring check. A simple approach is to schedule a test event every hour that writes a timestamp to a log file or a database option. Then check that timestamp daily. If the timestamp is more than an hour old, cron is not running. This is a low-tech but effective early warning system.

For teams that depend on scheduled imports or content expiration, consider moving those jobs out of WordPress entirely. A server-side cron job can call a custom PHP script that uses the WordPress REST API or a direct database connection. That removes the dependency on WP-Cron and gives you full control over execution time and error handling.

FAQ

How do I know if my host disabled real cron?

Check wp-config.php for define('DISABLE_WP_CRON', true);. Then run the Site Health loopback test. If the constant is set or the loopback test fails, WP-Cron is not running on page loads. You can also install WP Crontrol and look for overdue events.

Can I just remove DISABLE_WP_CRON from wp-config.php?

Only if you are on a host that does not provide a system cron replacement. On shared hosting, removing the constant restores page-load cron, but that is unreliable. On managed hosts, the constant is often required and removing it can cause duplicate cron runs or conflicts with the host’s own cron system.

What is the best interval for a system cron job?

Every 5 to 10 minutes is sufficient for most publishing teams. A shorter interval increases server load and risks overlapping runs. A longer interval delays scheduled events. If you have time-sensitive jobs, use a dedicated system cron entry for those jobs instead of shortening the global WP-Cron interval.

Why are my scheduled posts publishing at the wrong time?

Check the site’s timezone in Settings → General. Then check the plugin or theme code that schedules the event. If the code uses strtotime() or time() without the WordPress timezone, the timestamp will be wrong. Use wp_date() or DateTime with the WordPress timezone explicitly set.

For more on diagnosing WordPress failures, see What to Fix First When a New WordPress Site Says Nothing Found.

Server rack with blinking lights, representing cron job infrastructure
Close-up of a server motherboard, representing system-level debugging
Person working on a laptop with code on screen, representing WordPress debugging