“I scheduled the post for 9am and it went live at 2pm.” I’ve had that exact conversation with more than one client, and their first assumption is always that something is broken. Nothing is broken. WordPress’s built-in scheduler — called cron, borrowed from the Unix term for a job that runs automatically on a timer — was never designed to fire at the exact minute you asked it to. Here’s what it’s actually doing instead, and how to make it behave.
WP-Cron is triggered by visitors, not by time
Real system cron, the kind that runs on a Linux server, ticks along on its own clock independent of anything else happening on the machine. WordPress doesn’t have access to that by default, so it fakes the effect instead. Every time a page loads on your site, WordPress quietly asks itself one question: is anything scheduled that’s due to run right now?
That check happens on nearly any page load — a visitor reading a post, someone browsing your shop, even a bot crawling a page. If the answer is yes, WordPress fires off a separate, invisible request to a file called wp-cron.php in the background to actually run those tasks. That covers things like publishing scheduled posts, sending scheduled email digests, running a plugin’s cleanup job, and checking for available updates.
People call this “pseudo-cron” for a reason. It works fine on a site that gets a steady trickle of visitors all day, because there’s almost always a page load happening near the scheduled time to trigger it. It falls apart in two common situations:
- Low-traffic sites — a personal blog, an internal company site, a brand-new site with no visitors yet. If nobody loads a page between 9am and 2pm, nothing ever checks whether that 9am post is due.
- Sites behind aggressive full-page caching, where cached pages get served straight from a cache or CDN without ever touching PHP. The WP-Cron check simply never runs, because WordPress itself never gets asked to build the page.
So “the scheduled post published late” almost always really means “nobody visited a page until 2pm.”
The fix: disable pseudo-cron, use real cron
The reliable fix is to stop WordPress from trying to trigger cron off page loads at all, and instead have your server hit wp-cron.php on an actual fixed schedule — the same way real system cron works.
Step 1 — disable the pseudo-cron trigger
Add this line to wp-config.php — the file that holds your site’s core settings — just above the line that says /* That's all, stop editing! */:
define( 'DISABLE_WP_CRON', true );
This stops WordPress from spawning that background request on every page load. It’s one of a handful of small, high-value lines I add to wp-config.php on nearly every install I set up, alongside a few others I’ve written up separately in the wp-config.php settings I change every time. Just remember: after adding this, scheduled tasks will only run when something explicitly calls wp-cron.php — which is exactly what step 2 sets up.
Step 2 — set up a real system cron job
Most hosts give you a “Cron Jobs” section somewhere in cPanel, Plesk, or a similar control panel. Point it at wp-cron.php every 5 minutes — adjust that number to your needs, every minute if you publish time-sensitive content, or every 15 minutes if precision doesn’t matter much:
*/5 * * * * curl -s https://example.com/wp-cron.php >/dev/null 2>&1
If curl isn’t available on your host, wget does the same job:
*/5 * * * * wget -q -O - https://example.com/wp-cron.php >/dev/null 2>&1
If your host gives you SSH access — a command-line connection to your server — and WP-CLI is installed, this is the version I actually prefer. It runs cron directly through PHP rather than making a round trip over the web, and it only runs jobs that are actually due:
*/5 * * * * cd /path/to/your/site && wp cron event run --due-now >/dev/null 2>&1
Replace /path/to/your/site with the actual folder that contains your wp-config.php, and adjust the WP-CLI path if it isn’t already available in your system’s PATH.
A couple of things worth knowing
If you have WP-CLI, you can see exactly what’s currently scheduled with:
wp cron event list
This is genuinely useful for debugging. You’ll often find plugins have quietly scheduled far more recurring events than you’d expect.
There’s also a constant called WP_CRON_LOCK_TIMEOUT, which controls how long WordPress waits before allowing another cron spawn to start if one is already thought to be running — 60 seconds by default. You generally don’t need to touch it. But if you ever see cron jobs seemingly not firing because a previous one appears “stuck,” it’s worth knowing this setting exists. It’s a similar idea, on a much shorter timescale, to the stuck flag file behind a site stuck showing “briefly unavailable for scheduled maintenance” — both are cases where WordPress leaves a marker behind to say a job is in progress, and something keeps that marker from clearing.
One mistake I’ve seen more than once: people add DISABLE_WP_CRON and then forget to actually set up the real cron job afterward. That’s worse than doing nothing — now scheduled tasks never run at all, instead of just running late. If you add that constant, the system cron job isn’t optional. It’s the other half of the fix, and the two only work as a pair. If you’re not confident making this change directly on your live site, test it on a copy first — a staging site is the safe place to confirm your cron job is actually firing before you touch production.
Finally, if you’re on managed WordPress hosting — WP Engine, Kinsta, and similar — check their documentation first. Several of these hosts already run a real server-side cron for you in the background, and either ignore or override DISABLE_WP_CRON entirely. You may not need to do anything beyond confirming it’s enabled in their dashboard.
Frequently Asked Questions About WP-Cron and Scheduled Posts
Why did my scheduled post publish hours late instead of on time?
Almost always because nobody visited your site between the scheduled time and when it actually went live. WordPress only checks for due tasks when a page loads, so on a quiet site there can be a long gap between the scheduled time and the next visitor triggering that check.
Do I need a developer to fix this?
Setting up the real cron job usually just means pasting one line into your hosting control panel’s “Cron Jobs” section, which most people can do themselves once they know where to look. The wp-config.php edit is a single line too, though if you’re not comfortable editing site files directly, asking a developer to do this five-minute change is entirely reasonable.
Will disabling WP-Cron break anything on my site?
Not if you set up the replacement cron job at the same time. The danger is doing only half the fix — disabling the built-in trigger without adding a real cron job means scheduled tasks stop running entirely, rather than just running late.
How do I know if this is actually my site’s problem?
If your scheduled posts, or a plugin’s scheduled emails and cleanup jobs, are consistently late rather than occasionally late, it’s very likely this. Low-traffic periods (overnight, weekends) are when it shows up most, since fewer visitors means fewer chances for WordPress to notice a task is due.
What if I’m on managed hosting like WP Engine or Kinsta?
Check their dashboard or documentation before making any changes yourself. Many managed hosts already run a real server-level cron for every site they host, which means the fix described here may already be handled for you.
Can I just make WP-Cron run more often instead of switching to real cron?
There isn’t a reliable way to make the page-load-triggered version run on a fixed schedule — that’s the whole limitation. Real system cron is the only way to guarantee tasks run at a specific time regardless of whether anyone visits your site.
Once real cron is wired up, scheduled posts, plugin cleanup jobs, and email digests all start firing on time instead of “whenever the next visitor happens to show up.” It’s a five-minute setup that fixes a problem most site owners never even realize has a name.
Leave a Reply