WordPress Scheduled Posts Not Publishing? Fix WP-Cron
You scheduled a post for 9 AM. It's now 11 AM and it's still sitting in the queue with a red "Missed Schedule" label instead of going live. If this happens once, it's a fluke. If it happens every time, the problem isn't the post - it's WP-Cron, and it's worth fixing properly instead of just clicking "Publish" manually every time.
Symptom: Posts Stuck on "Missed Schedule"
You'll usually notice this in one of a few ways:
- The post list shows Missed Schedule in red instead of the scheduled date.
- Plugin update notifications don't refresh even though updates are available.
- Scheduled backups, WooCommerce abandoned-cart emails, or SEO sitemap regeneration silently stop running.
- The site feels fine otherwise - no errors, no white screen - just certain time-based things quietly not happening.
All of these point to the same root cause: WP-Cron isn't firing when it's supposed to.
Cause: WP-Cron Isn't a Real Cron Job
This is the part that catches people out. WordPress doesn't have its own background scheduler talking to the operating system. Instead, every time wp-cron.php loads - which normally happens on a visitor page request - WordPress checks whether any scheduled task is due and runs it right then.
That design works fine for busy sites getting constant traffic. It falls apart in a few common situations:
1. Low Traffic
If nobody visits your site between 8 AM and 11 AM, nothing triggers wp-cron.php, so your 9 AM scheduled post just sits there until the next visitor shows up - sometimes hours later.
2. Page Caching
Full-page cache plugins (WP Super Cache, LiteSpeed Cache, W3 Total Cache) serve a cached HTML file directly, bypassing PHP entirely for most visits. If PHP never runs, WP-Cron never gets a chance to check its schedule.
3. DISABLE_WP_CRON Set but No Replacement Configured
Some security plugins or hosting setups add this line to wp-config.php to stop the pseudo-cron from firing on every page load (which is good for performance) - but only if you also set up a real server cron to replace it. Miss that second step and scheduling just stops working entirely.
define('DISABLE_WP_CRON', true);
4. Security Plugins Blocking wp-cron.php Requests
Some hardening plugins or firewall rules treat repeated requests to wp-cron.php as suspicious and rate-limit or block them, especially on sites that were previously hit by scanners hammering that file.
5. A Slow or Timed-Out Site
If a page request takes too long - a slow plugin, a database query gone wrong - the request can time out before wp-cron.php finishes its checks, so the task never actually completes even though it technically "ran."
Fix: Confirm It, Then Replace It With a Real Cron Job
Step 1: Confirm WP-Cron Is the Problem
Install the free WP Crontrol plugin, or check via Site Health:
- Go to Tools → Site Health → Info → WordPress Constants and check whether
DISABLE_WP_CRONis set. - With WP Crontrol installed, go to Tools → Cron Events - you'll see a list of scheduled hooks and whether any show as overdue (a red "Due" past its scheduled time is your smoking gun).
You can also test directly by visiting this URL in a browser (replace with your domain):
https://yourdomain.com/wp-cron.php?doing_wp_cron
A blank white page with no errors means it's at least reachable. A 403, 404, or timeout tells you something (a firewall rule, a security plugin, or a slow server) is blocking it before WordPress even gets a chance to check the schedule.
Step 2: Disable the Pseudo-Cron
Open wp-config.php via File Manager or SFTP and add this line above the /* That's all, stop editing! */ comment:
define('DISABLE_WP_CRON', true);
This stops WordPress from trying (and failing) to run its schedule on every page load, which also shaves a small amount of load time off each request.
Step 3: Add a Real Cron Job in cPanel
Log in to cPanel and go to Advanced → Cron Jobs. Set it to run every 15 minutes and use one of these two commands, depending on what your host allows:
| Method | Command |
|---|---|
| wget (most common) | wget -q -O /dev/null "https://yourdomain.com/wp-cron.php?doing_wp_cron" >/dev/null 2>&1 |
| WP-CLI (if available on your account) | cd /home/yourcpanelusername/public_html && /usr/local/bin/wp cron event run --due-now --quiet |
If your site lives in a subdirectory rather than the account root, adjust the path or URL accordingly. Every 15 minutes is a good default - tight enough that scheduled posts go out close to on time, loose enough that it doesn't add meaningful server load.
Step 4: Verify It's Actually Firing
Schedule a test post 20-30 minutes out, then walk away from the site (don't refresh it - that would trigger the old pseudo-cron behavior and mask the test). Check back after the scheduled time. If WP-CLI is available, you can also just run:
wp cron event list --due-now
An empty list after your cron job has run means everything due has been processed.
Prevention
- Don't stack DISABLE_WP_CRON on top of an unconfigured cron job. If you disable the pseudo-cron, the server cron job is not optional - set both in the same maintenance window.
- Whitelist wp-cron.php in any security plugin or firewall rule that rate-limits requests, so your own scheduled cron hits don't get blocked.
- Keep the interval reasonable. Every 1-5 minutes is overkill for most sites and just adds unnecessary load; every 15 minutes covers scheduled posts, WooCommerce emails, and plugin checks comfortably.
- Re-check after migrations. A move to Getwebup or any new host resets your cPanel cron jobs - they don't carry over automatically, so this is an easy thing to forget after a migration.
- Watch for it after adding full-page caching. If you add a caching plugin later and scheduled posts stop firing again, this is almost always why - the cache is now the thing serving most requests instead of PHP.
Frequently asked questions
Will disabling DISABLE_WP_CRON break anything if I don't set up a replacement cron job?
Yes - scheduled posts, plugin cron tasks, and anything time-based in WordPress will simply stop running because nothing is left to trigger them. Only add that constant once the real server cron job in cPanel is already working.
How often should the cPanel cron job run?
Every 15 minutes is a good default for most sites. Going more frequent than every 5 minutes rarely helps and just adds extra load for no real benefit.
Can I just manually publish the post instead of fixing WP-Cron?
You can, but it doesn't scale - WooCommerce order emails, scheduled backups, SEO plugin tasks, and license checks all rely on the same mechanism, so the underlying problem keeps causing new symptoms.
I set up the cron job but posts still show Missed Schedule. What now?
Check that DISABLE_WP_CRON wasn't left set without the cron job actually saved correctly, confirm the domain and path in the wget command are exact, and test the wp-cron.php URL directly in a browser to rule out a firewall or security plugin blocking the request.
Does this affect WooCommerce or just blog posts?
It affects anything WordPress schedules through the same system - WooCommerce abandoned cart emails, subscription renewals, scheduled backups, and SEO sitemap regeneration all run through the identical WP-Cron mechanism.