You hit "Schedule" on a post, set it to go live at 9 AM, and walk away. Nine o'clock comes and goes. The post is still sitting in your drafts list with a red label: Missed schedule. Nothing crashed, no error email, WordPress just... didn't do the thing you told it to do. This is almost always a WP-Cron problem, and it's one of the more misunderstood parts of WordPress.
Symptom: Posts Stay in "Missed Schedule"
The pattern is usually one of these:
- A scheduled post sits past its publish time with a "Missed schedule" tag, and only goes live once you manually click "Publish" or someone visits the site.
- Plugin auto-updates, scheduled backups, or SEO sitemap regeneration stop running on time — or stop running at all.
- WooCommerce abandoned-cart emails or subscription renewals fire late, sometimes hours late.
- Everything works fine right after you visit the dashboard, then goes quiet again for the rest of the day.
That last point is the giveaway. WP-Cron isn't a real background service — it's a trick, and once you see the trick, the whole problem makes sense.
Cause: WP-Cron Only Runs When Someone Visits
Real Linux cron (the kind covered in our cPanel cron jobs guide) runs on a fixed schedule whether or not anyone is looking at the server. WordPress doesn't have that by default. Instead, every time a visitor loads any page on your site, WordPress checks a file called wp-cron.php and asks: "is anything due to run right now?" If yes, it fires those tasks as part of that page load, in the background, before sending the page to the visitor.
This works fine on a busy site getting hit every few seconds. It falls apart in a few common situations:
- Low-traffic sites. A blog getting a handful of visits a day might go hours between page loads — and hours between WP-Cron checks. A post scheduled for 9 AM doesn't publish until the first visitor shows up at noon.
- Full-page caching. If a caching plugin or CDN serves a cached HTML file straight from disk, WordPress never actually boots for that request, so
wp-cron.phpnever gets triggered. Heavily cached sites can go days without a single real cron check. - DISABLE_WP_CRON set and nothing replacing it. Some hosts or performance plugins add
define('DISABLE_WP_CRON', true);towp-config.phpto stop the pseudo-cron from firing on every page load — which is good for performance, but only if you set up a real cron job to replace it. Skip that second step and scheduled tasks just stop. - A slow or broken task blocking the queue. WP-Cron runs its due tasks somewhat sequentially during that page load. If one task hangs (a plugin calling a slow external API, for example), it can hold up or time out before your post-publish task even runs.
Fix: Replace the Pseudo-Cron with a Real One
The reliable fix is the same one performance guides recommend anyway: turn off the page-load trigger and let a real system cron job hit wp-cron.php on a fixed schedule instead.
Step 1 — disable the default trigger. Add this line to wp-config.php, above the line that says /* That's all, stop editing! */:
define('DISABLE_WP_CRON', true);
Step 2 — add a real cron job. In cPanel, go to Advanced > Cron Jobs and add a job that runs every 15 minutes:
*/15 * * * * curl -s https://yourdomain.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1
Or, if WP-CLI is available on your account (common on SkyServer VPS plans), this is lighter on resources than a full HTTP request:
*/15 * * * * cd /home/username/public_html && /usr/local/bin/wp cron event run --due-now --quiet
Fifteen minutes is a good default — tight enough that scheduled posts and emails go out close to on time, loose enough not to hammer the server. If you're running WooCommerce or something time-sensitive, you can drop it to every 5 minutes.
Step 3 — verify it's actually running. Install a small free plugin like WP Crontrol (or use WP-CLI: wp cron event list) to see what's scheduled and when it last ran. If the "next run" time keeps updating on its own without you touching the dashboard, the real cron job is doing its job.
Step 4 — if you're not using WP-CLI or cPanel cron, at minimum remove the DISABLE_WP_CRON line and instead use a free external uptime/ping service to hit your homepage every few minutes. It's not as clean as a real cron job, but it forces the pseudo-cron trigger to fire regularly even with zero real visitors.
Prevention: Keep It From Happening Again
- Don't set
DISABLE_WP_CRONwithout adding the replacement cron job in the same sitting. Half-doing this is worse than doing nothing, since it's exactly what causes the silent failures customers report. - Check cron health after migrations. Moving to SkyServer or changing PHP versions can wipe out cPanel cron jobs if they weren't part of your backup. Confirm the job is still listed under Cron Jobs after any migration.
- Watch for plugin conflicts. Some caching and security plugins disable WP-Cron themselves without telling you clearly. If posts stop publishing right after installing a new plugin, check its settings for a "disable WP-Cron" toggle before assuming it's a server issue.
- Keep an eye on task count. A site with dozens of plugins each scheduling their own hourly or daily events can end up with a bloated cron queue. WP Crontrol lets you see and clean up orphaned events left behind by uninstalled plugins.
Frequently Asked Questions
Will disabling WP-Cron break anything if I forget the replacement cron job?
Yes — this is the single most common cause of the problem in the first place. If you set DISABLE_WP_CRON to true and don't add a real cron job hitting wp-cron.php, no scheduled WordPress task will ever run automatically again. Always do both steps together.
How do I know if my host already disabled the default WP-Cron trigger?
Check wp-config.php for a DISABLE_WP_CRON line, or install WP Crontrol and look at the cron health notice on its events page — it will tell you directly whether WordPress thinks its own pseudo-cron is active.
Is it safe to run wp-cron.php every minute instead of every 15?
Technically yes, but it's usually overkill and adds unnecessary load, especially on shared hosting with CPU limits. Every 15 minutes covers scheduled posts and most plugin tasks comfortably; drop to 5 minutes only if you're running time-sensitive WooCommerce or membership workflows.
My site has heavy caching — does that make this worse?
Yes. Full-page caching (server-side or CDN) can serve pages without WordPress ever booting, which means the pseudo-cron trigger never fires no matter how much traffic you get. This is exactly why cached, high-traffic sites still need a real system cron job rather than relying on visits.
Can I check whether a specific scheduled post is stuck without installing a plugin?
If you have SSH or terminal access and WP-CLI installed, run wp post list --post_status=future to see upcoming scheduled posts and their times, or wp cron event list to see the raw cron queue. That's usually faster than digging through the dashboard.
