WP-Cron doesn’t run on a schedule the way most people assume. It runs when a visitor loads a page, which is a clever workaround for shared hosting that never had real cron access, and it’s also the source of most of the confusion and half-working setups this topic generates. Understanding that one fact clears up almost everything else.
Once that clicks, deciding whether to change anything gets a lot simpler.
How WP-Cron Actually Works
Every WordPress page load includes a quiet check: does anything scheduled (a due post, a plugin’s recurring task, a backup job) need to run right now? If yes, WordPress spins up a background request to wp-cron.php that fires those tasks, then the original page continues loading normally for the visitor.
This means WP-Cron’s actual firing frequency is tied entirely to traffic. A busy site effectively runs cron checks every few seconds without anyone configuring anything. A quiet site with a visitor every few hours might have scheduled tasks sit overdue for that same stretch, since nothing triggers the check until someone actually loads a page.
When This Actually Becomes a Problem
High-traffic sites see the opposite issue: near-constant cron checks add real, measurable overhead, since every single page load is doing extra work checking the schedule even on the overwhelming majority of loads where nothing is actually due. On a busy WooCommerce store or a high-traffic membership site, this adds up to meaningful, avoidable server load.
Low-traffic sites see delayed or missed scheduled tasks: a post scheduled for 9am publishes at 2pm because that’s when the first visitor of the day showed up. A nightly backup plugin silently doesn’t run some nights because no page load happened to trigger it during the right window.
Neither problem is universal, and a decent number of sites never notice either issue at typical traffic levels. Check whether you’re actually experiencing one of these before assuming a fix is needed.
Disabling WP-Cron
Add this to wp-config.php, above the line that reads “That’s all, stop editing”:
define(‘DISABLE_WP_CRON’, true);
This stops WordPress from spawning that background request on every page load. It does not stop scheduled tasks from existing, they’re still queued in the database, just nothing triggers them anymore until you set up a replacement.
Setting Up a Real Server Cron Job
This is the step people skip, and skipping it means scheduled posts stop publishing and plugin maintenance tasks stop running entirely, with no error message telling you why.
In your hosting control panel’s Cron Jobs section (commonly under Advanced in cPanel), add a new job that runs on your chosen interval, typically every 5 to 15 minutes is reasonable for most sites, with this command:
wget -q -O /dev/null “https://yoursite.com/wp-cron.php?doing_wp_cron” >/dev/null 2>&1
Or, if WP-CLI is available on your host, the more efficient equivalent:
cd /path/to/wordpress && wp cron event run –due-now
The WP-CLI version is preferable where available since it runs cron logic directly through PHP’s CLI without the overhead of a full HTTP request round-trip, and it doesn’t count against any rate limiting or bot protection that might otherwise flag repeated automated requests to wp-cron.php.
Choosing the Right Interval
Every 5 minutes is a common, safe default for most sites. If the site relies on precisely timed scheduled posts (a news site publishing on the minute, for instance), a tighter interval like every 1 to 2 minutes gets closer to exact timing, at the cost of slightly more frequent server jobs running, most of which will find nothing due and exit immediately with negligible actual load.
For sites where scheduled task timing doesn’t need to be precise (routine plugin maintenance, cache cleanup), a longer interval like every 15 to 30 minutes reduces server job frequency without meaningfully affecting anything visitors would notice.
The Mistake That Causes the Most Support Tickets: Forgetting Step Two
Adding DISABLE_WP_CRON without setting up a replacement cron job is by far the most common way this goes wrong. The site looks fine initially, since nothing breaks immediately and no error appears. Then, days or weeks later, someone notices scheduled posts aren’t publishing, a backup plugin hasn’t run in a while, or an email digest stopped sending, and it takes real troubleshooting time to trace it back to a cron setting changed weeks earlier and half-forgotten.
If you’re going to disable WP-Cron, set up the replacement server cron job in the same session, immediately, not as a follow-up task for later. Test it works by checking whether a manually scheduled test post actually publishes at its set time before considering the change complete.
ALTERNATE_WP_CRON: A Middle Ground Worth Knowing
There’s a third option between “leave WP-Cron running on every page load” and “disable it entirely for a server cron job”: ALTERNATE_WP_CRON. Add this instead of DISABLE_WP_CRON:
define(‘ALTERNATE_WP_CRON’, true);
This changes how the background cron request fires, using a redirect-based approach instead of a separate async HTTP request, which works around certain hosting environments (some firewalls or security configurations) that block WordPress’s normal loopback request mechanism, causing WP-Cron to silently fail to fire at all even without DISABLE_WP_CRON set. This is a narrower fix for a specific symptom (cron not firing due to blocked loopback requests) rather than a general performance optimization, worth trying specifically if scheduled tasks aren’t running and you haven’t set DISABLE_WP_CRON yourself.
Multi-Server and Load-Balanced Setups Need Extra Care
If the site runs across multiple web servers behind a load balancer, a server cron job configured on only one server is the correct approach, not one per server. Setting up the same cron job independently on multiple servers in a load-balanced cluster causes the same scheduled task to fire multiple times simultaneously, which can create duplicate posts, duplicate emails, or duplicate order processing depending on what the scheduled task actually does.
Confirm with whoever manages the server infrastructure which single server (or a dedicated cron/worker instance separate from the web-serving nodes entirely) is the correct place for this job before assuming each server needs its own identical cron entry.
Monitoring Whether Cron Is Actually Running
Don’t just set it and assume it works forever. WP Crontrol, a free plugin, shows every scheduled cron event, when it’s due, and when it last ran, directly in wp-admin. This turns “is my cron actually firing” from a guessing game into a two-minute check.
Check this after initially setting up a server cron job, and periodically afterward, particularly after any hosting change or migration, since server-level cron configurations don’t always survive a move to a new host and can silently stop working with nothing on the WordPress side indicating anything is wrong.
What Happens to Tasks That Were Already Overdue When You Made the Switch
If scheduled tasks had already backed up before you set up the server cron job (a common scenario if you’re fixing a site where WP-Cron was quietly failing for a while before anyone noticed), the first run of the new cron job will process everything that’s overdue, potentially all at once. This is usually fine for most tasks, but worth being aware of if one of the overdue items is something with a visible side effect, like a batch of scheduled posts all publishing within the same minute instead of spread across their original intended times.
Check WP Crontrol’s event list before the first cron run after switching, so you know what’s queued and roughly what to expect, rather than being surprised by a cluster of posts or emails firing together.
Using systemd Timers Instead of Traditional Cron
On servers managed with systemd (common on more recent Linux distributions), a systemd timer unit is a modern alternative to a traditional crontab entry, with better logging and easier failure diagnosis built in. This is more relevant for VPS or dedicated server setups where you manage the OS directly, rather than shared hosting where you’re working through a hosting panel’s cron interface.
A basic timer unit pointed at a small script running the same wp cron event run –due-now command gives you systemd’s own logging (journalctl -u your-timer-name) to check execution history and failures, which is often more informative than what a shared host’s cron panel exposes. This is worth the extra setup specifically on infrastructure you control directly and where reliability matters enough to want better observability than a basic cron log line.
Third-Party Uptime and Cron Monitoring Services
For sites where a missed cron run has real consequences (a membership site’s renewal processing, a store’s abandoned cart recovery), consider a dedicated cron monitoring service that pings you if the expected job doesn’t check in within its expected window. Several free and low-cost services exist specifically for this (searching “cron monitoring” turns up several established options), working by having your cron job hit a unique monitoring URL after it completes, and alerting you if that check-in doesn’t happen on schedule.
This closes the gap that WP Crontrol alone doesn’t cover: WP Crontrol tells you what’s scheduled and when it last ran if you check manually, but it doesn’t proactively alert you the moment something stops firing. A monitoring service does exactly that, and for anything revenue-connected, it’s worth the modest setup effort.
A Real Troubleshooting Scenario: A Newsletter That Sends Hours Late
A site owner sets a newsletter plugin to send every subscriber update at 8am. For the first few weeks it goes out close enough to on time that nobody notices the drift. Then a slow traffic month hits, the site’s first visitor some mornings doesn’t show up until well past 10am, and subscribers start asking why the newsletter is arriving at lunchtime instead of with their morning coffee.
This is the classic default WP-Cron symptom, not a bug in the newsletter plugin itself. The scheduled task exists and is correctly configured for 8am. Nothing is actually checking whether it’s due until a page load happens to trigger that check, and on a quiet morning, that first page load might be hours later than the task’s intended time.
Confirm this diagnosis before changing anything: install WP Crontrol, find the newsletter plugin’s scheduled event in its list, and compare its “next run” time against when it actually fired the previous few sends, visible either in Crontrol’s history if the plugin logs it, or in the newsletter plugin’s own send log. A consistent pattern of firing late specifically on the site’s lowest-traffic days, and closer to on time on busier days, confirms it’s page-load-triggered drift rather than a setting misconfigured inside the newsletter plugin itself.
The fix is exactly the two-step process covered above: set DISABLE_WP_CRON in wp-config.php, then add a real server cron job hitting wp-cron.php (or running wp cron event run –due-now via WP-CLI) on a five-minute interval. Once that’s live, the newsletter’s 8am task fires within five minutes of 8am regardless of whether a single visitor has loaded the site yet that morning, since a server-level cron job doesn’t depend on site traffic to trigger at all. Verify with WP Crontrol again after the next scheduled send, confirming the actual fire time now sits close to the intended one, rather than assuming the server cron job is working just because no error appeared when it was set up.
Common Mistakes Worth Naming Directly
Disabling WP-Cron without setting up a replacement server cron job, causing scheduled posts and maintenance tasks to silently stop.
Setting an identical cron job on every server in a load-balanced cluster, causing scheduled tasks to fire multiple times per interval.
Assuming ALTERNATE_WP_CRON and DISABLE_WP_CRON do the same thing; they’re separate fixes for separate symptoms and shouldn’t both be set at once.
Never checking whether the replacement cron job is actually running after the initial setup, and only discovering it silently broke weeks later.
Managed WordPress Hosting Often Handles This For You Already
A number of managed WordPress hosts (Kinsta, WP Engine, and several others) disable default WP-Cron behavior and run their own server-level cron automatically as part of the platform, without requiring you to configure anything yourself. Check your specific host’s documentation before manually setting DISABLE_WP_CRON, since some platforms already handle this and adding your own conflicting configuration on top can cause confusion, or in some cases duplicate cron execution if both your manual setting and the host’s built-in system end up active simultaneously.
If you’re not sure whether your host already handles this, check WP Crontrol’s event log for signs cron is firing reliably before assuming you need to configure anything at all. A number of “how do I fix WP-Cron” support requests turn out to be solved already at the hosting level, and the fix was never needed in the first place.
Debugging a Cron Job That Isn’t Firing
If you’ve set up the server cron job and WP Crontrol still shows events sitting overdue, work through this in order. Confirm the cron job actually exists in the hosting panel and hasn’t been accidentally deleted or disabled during a hosting change. Confirm the URL or WP-CLI path in the cron command is correct and current, particularly after any site migration where the domain or server path might have changed.
Check whether a security plugin or firewall rule is blocking requests to wp-cron.php specifically, since some security hardening guides recommend blocking direct external access to wp-cron.php without accounting for the fact that a legitimate server cron job needs to reach that same file. If a firewall rule is the culprit, an exception for your own server’s IP or for the specific cron command’s origin resolves it without weakening the broader security posture.
FAQ
Will disabling WP-Cron break scheduled posts immediately?
Yes, the moment DISABLE_WP_CRON is set without a working replacement in place. Set both at the same time, and verify the replacement works before considering the change finished.
How do I know if WP-Cron is currently causing a performance problem on my site?
Check your server’s access logs or a monitoring tool for how often wp-cron.php is being hit, or use a profiling tool like Query Monitor to see if cron checks are adding measurable time to page loads. On most small to medium sites this overhead is genuinely negligible and not worth the added complexity of managing a separate server cron job.
Does WooCommerce or membership plugins rely heavily on WP-Cron?
Yes, subscription renewals, abandoned cart emails, and various scheduled maintenance tasks in WooCommerce and most membership plugins run through WP-Cron by default. This makes a reliable, correctly configured cron setup more important on e-commerce and membership sites than on a simple content blog.
Can I test whether my server cron job is actually hitting wp-cron.php correctly?
Yes, check your server’s raw access logs for requests to wp-cron.php around the interval you configured. If they’re not showing up, the cron job itself likely isn’t running or has the wrong URL, a permissions issue, or an incorrect path, separate from any WordPress-side configuration.
Does setting a shorter cron interval, like every minute, cause any real downside?
For most sites, no, a job that finds nothing due exits almost instantly with minimal resource use. The main reason not to go too aggressive is unnecessary log noise and, on some hosts, a soft cap on how many scheduled jobs or how frequent an interval the panel allows. Every 5 minutes is a reasonable default that balances timing precision against job frequency for the overwhelming majority of sites.
Where This Leaves You
Most sites don’t need to touch WP-Cron at all, the default behavior is fine at typical traffic levels.
If you do disable it, for real performance reasons on a high-traffic site or because scheduled tasks are consistently late on a low-traffic one, set up the replacement server cron job in the same session. Don’t leave it for later.
Verify it’s actually firing with WP Crontrol, and revisit that check after any hosting migration. This is exactly the kind of setting that silently breaks and stays broken until someone notices something else went wrong first, usually a scheduled post that never went live.