If a post is sitting in your Posts list with a red Missed schedule where the publish date should be, WordPress did not fail to publish it. WP-Cron simply never ran at the moment the post was due, so the post is still parked at status future with its content completely intact. Publishing it takes about ten seconds. Stopping it from happening again next Tuesday is the part worth reading.
Below is what the missed schedule error actually is, the causes ordered by how often they turn out to be the culprit, the WP-CLI commands that confirm each one, and the permanent fix. Everything here was checked against WordPress 7.1 running on PHP 8.3.
What the missed schedule error looks like
There is no error page and no email. The only signal is in the Date column of Posts → All Posts:
Title Date
───────────────────────────── ─────────────────────
Autumn sale announcement Missed schedule
2026/09/03 at 9:00 am
The words “Missed schedule” come straight from WordPress core. The list table compares the post’s scheduled timestamp against the current time, sees that a post still marked future is overdue, and prints that instead of the date. Nothing is corrupt, nothing is lost, and no database repair is needed.
Why the missed schedule error happens at all
When you schedule a post, WordPress does two things. It sets the post status to future, and it registers a single cron event called publish_future_post, carrying that post’s ID, timestamped for the moment you chose.
Here is the part that surprises people: WP-Cron is not a cron daemon. It is a queue stored in the cron row of your wp_options table, and nothing inspects that queue on its own. WordPress only looks at it when somebody loads a page. On that page load, core fires a non-blocking loopback request back to your own site at /wp-cron.php, and that request runs whatever is due.
So the chain has three links that can break independently:
- Somebody has to hit the site at roughly the right time.
- The loopback request to
wp-cron.phphas to actually reach your server. - The queue has to get far enough down the list to reach your
publish_future_postevent before PHP runs out of time.
Break any one of them and the post stays at future forever. WordPress never retries on its own.
The causes, in the order they are usually to blame
| Cause | How you confirm it | Fix |
|---|---|---|
1. DISABLE_WP_CRON is true and no server cron replaced it | wp config get DISABLE_WP_CRON returns 1, and crontab -l shows nothing for the site | Add the real cron job, or remove the constant |
| 2. Not enough traffic | Events fire hours late rather than never; the site gets a handful of visits a day | Server cron every 5 minutes |
| 3. Loopback requests are blocked | Site Health flags the loopback or scheduled-events test; wp cron test returns an error | Unblock the loopback, or bypass it with server cron |
| 4. One slow event is jamming the queue | wp cron event list shows a pile of overdue events behind one hook | Run it manually, or delete the stuck hook |
5. A stale doing_cron lock | Nothing at all has run for hours despite traffic | wp transient delete doing_cron |
| 6. Your host runs cron on a long interval | Managed hosts often disable WP-Cron and run their own every 15–60 minutes | Shorten it in the host panel, or run your own |
| 7. Wrong site timezone (the look-alike) | The post did publish — just at an hour you did not expect | Settings → General → Timezone |
Before you edit wp-config.php: take a copy of it first. A single missing semicolon in that file takes the whole site down, and you will be fixing it over SFTP with no admin access. Download wp-config.php to your desktop, or run cp wp-config.php wp-config.php.bak over SSH, before changing a line.
Diagnose it with five WP-CLI commands
If you have SSH access, this takes two minutes and tells you which of the seven causes you have. Run these from your WordPress root.
# 1. Can WordPress spawn its own cron at all?
wp cron test
# 2. What is in the queue, and how overdue is it?
wp cron event list
# 3. Which posts are stuck at "future"?
wp post list --post_status=future --fields=ID,post_title,post_date
# 4. Is WP-Cron switched off in wp-config.php?
wp config get DISABLE_WP_CRON
# 5. Is a stale lock holding everything up?
wp transient get doing_cron
Reading the output:
wp cron testprinting Success: WP-Cron spawning is working as expected means the loopback is fine and your problem is cause 1, 2 or 4. An error here points straight at cause 3.- In
wp cron event list, look at thenext_run_relativecolumn. A value like-2 daysornowon many rows means the queue is not being drained at all. One badly overdue hook with everything else current means cause 4, and the hook name usually tells you which plugin owns it. - If
wp config get DISABLE_WP_CRONreturns1and you have no server cron job, you have found it. This is the single most common cause on sites that were once “optimised” by a developer or a host.
No SSH? Two of these checks exist in the admin. Tools → Site Health → Status runs a scheduled-events test and a loopback test, and it will tell you plainly if events are late or if the loopback is failing. For the queue itself, the free WP Crontrol plugin lists every registered event with its next run time and lets you run one by hand — it is the standard tool for this and has been maintained for years.
Publish the stuck posts right now
Deal with the backlog first, then fix the cause. The cleanest way is to make the due events actually run, because that fires publish_future_post properly and every hook that depends on it — sitemap pings, newsletter triggers, cache purges — behaves as though the post had gone out on time:
# Run everything that is currently due
wp cron event run --due-now
# Or just the scheduled-post events
wp cron event run publish_future_post
If the event has been dropped from the queue entirely — it appears in neither wp cron event list nor WP Crontrol — there is nothing left to run, and you publish the post directly:
wp post update 1234 --post_status=publish
From the dashboard, the equivalent is to open the post, and either hit Publish or move the scheduled date to a minute in the past and update. Both work. Neither changes the URL or the content.
How to fix the missed schedule error permanently
Stop depending on visitors. Turn off the page-load trigger and let the operating system run the queue on a fixed interval. This is what every publisher with a real editorial calendar does, and it is two steps.
Step one. In wp-config.php, above the line that reads /* That's all, stop editing! Happy publishing. */, add:
define( 'DISABLE_WP_CRON', true );
Step two. Add a system cron job. Open your crontab with crontab -e, or use the Cron Jobs panel in cPanel, and add one of these:
# Option A — hit wp-cron.php over HTTP every five minutes
*/5 * * * * curl -sS -o /dev/null https://example.com/wp-cron.php?doing_wp_cron
# Option B — run the queue through WP-CLI (faster, no HTTP round trip)
*/5 * * * * cd /home/username/public_html && /usr/local/bin/wp cron event run --due-now --quiet
Three details that trip people up. Use your real domain, not localhost, in option A. Use absolute paths in option B — cron runs with a minimal environment and will not find wp on your interactive shell’s PATH; run which wp to get the real path. And five minutes is the right interval for a publishing site: one minute adds load for no benefit, fifteen means a post scheduled for 9:00 can go out at 9:14.
Never do step one without step two. Setting DISABLE_WP_CRON to true with no replacement job does not just break scheduled posts — it silently stops every recurring task on the site: update checks, Action Scheduler jobs in WooCommerce, backup runs, transient cleanup, subscription renewals. Sites limp along like this for months before anyone notices, and the first symptom is usually a missed schedule.
If your host will not give you a cron job
Entry-level shared plans sometimes hide the Cron Jobs panel. Two workarounds, in order of preference.
An external cron service. Leave DISABLE_WP_CRON set to true and have a third-party scheduler request https://example.com/wp-cron.php?doing_wp_cron every five minutes. Functionally identical to option A above, just triggered from somebody else’s machine. The trade-off is that a public URL being hit on a schedule is a small extra attack surface, so keep an eye on it if you are already chasing odd behaviour on the site.
Alternate cron. If loopback requests are what is broken — a staging site behind HTTP basic auth is the classic case — you can switch to the redirect-based mode instead:
define( 'ALTERNATE_WP_CRON', true );
This skips the loopback entirely. Instead, WordPress redirects the visitor’s own browser to a URL with ?doing_wp_cron appended, and the cron queue runs on that request. It works where nothing else will, but understand the cost before you reach for it: it appends a query string to a real visitor’s URL, which page caches and CDNs handle inconsistently, and it still needs traffic to fire. Treat it as a fix for locked-down staging environments, not as the answer for a production site.
When one plugin is jamming the queue
Cause 4 deserves its own note because the fix is different. WP-Cron works through the due list in sequence within a single PHP request. If one event takes 40 seconds — a feed importer, a big sitemap rebuild, an analytics roll-up — and PHP’s max_execution_time is 30, the request dies partway through and everything behind it in the queue never runs. Your post is behind it.
Core also holds a lock while cron is running, controlled by WP_CRON_LOCK_TIMEOUT, which defaults to 60 seconds. That is why the queue cannot be spawned more than once a minute, and why a run that dies mid-way can leave things quiet for a while afterwards. Raising that number does not help; it is a floor on how often cron starts, not a time limit on your events.
Find the offender and deal with it:
# Which hook is badly overdue?
wp cron event list
# Run just that one and time it
time wp cron event run some_plugin_daily_sync
# If it is orphaned — left behind by a deleted plugin — remove it
wp cron event delete some_plugin_daily_sync
Only delete a hook you can positively identify as orphaned. If it belongs to a plugin you still use, the answer is to fix or replace that plugin, and the process for pinning down which one is misbehaving is the same as for isolating any plugin conflict. A cron queue that starts backing up right after an update is worth reading alongside your notes on updating plugins without breaking the site, because the timing is rarely a coincidence.
Confirm the fix actually took
Do not declare victory because one post published. Test it deliberately:
- Create a throwaway draft and schedule it six minutes out. Then close the tab and do not touch the site — loading the admin yourself would trigger cron and prove nothing.
- Come back after eight minutes. It should be live, with no “Missed schedule”.
- Run
wp cron event listand check thatnext_run_relativevalues are in the future, not stacked up in the past. - Open Tools → Site Health and confirm the scheduled-events test is clean.
- Delete the test post.
One more thing worth checking while you are in there. WP-Cron leans on PHP being able to make an outbound request to your own domain, and on PHP not timing out mid-queue. If your site is on an old PHP build with a tight max_execution_time, cron problems tend to arrive alongside other intermittent failures, and checking and updating PHP safely often clears several of them at once. Make that change on staging and test the site properly afterwards rather than on the live install.
Frequently asked questions
Does the missed schedule error mean my post was lost?
No. The post is stored exactly as you wrote it, with the status future instead of publish. The content, images, categories and slug are all intact. Publishing it changes nothing except the status and the date.
Will a “missed schedule fix” plugin solve this?
Those plugins scan for overdue future posts on page load and publish them. That treats the symptom — the post goes out, just late and only once somebody visits — while every other scheduled task on your site stays broken. Fix the cron chain instead. If you want a plugin in the mix, make it one that shows you the queue, like WP Crontrol, rather than one that papers over it.
Why did my post publish, but an hour off?
That is a timezone problem, not a missed schedule. WordPress stores cron timestamps in UTC and converts for display using Settings → General → Timezone. If that is set to a UTC offset rather than a named city, it will not follow daylight saving, and your posts will drift by an hour twice a year. Pick the city, not the offset.
My host says they already run WP-Cron for me. Do I still need this?
Check the interval before you accept it. Several managed hosts disable WP-Cron and run their own on a 15-minute or hourly cycle, which is fine for update checks and useless for a post scheduled to the minute. Ask what the interval is, and whether you can shorten it. If you cannot, run your own job at five minutes and leave theirs alone.
Is it safe to visit wp-cron.php in my browser?
Yes. It is a normal WordPress endpoint that runs any due events and returns a blank page — a blank white screen there is success, not an error. It is a quick way to confirm the file is reachable and not blocked by a firewall rule. What you should not do is treat that as your scheduler; it only helps for as long as you keep clicking it.
The short version
Run wp cron event run --due-now to clear the backlog, then check whether DISABLE_WP_CRON is set without a server cron job behind it — that one combination accounts for most missed schedule errors you will ever see. If it is not that, run wp cron test to rule out a blocked loopback, then look for a single overdue hook holding the queue hostage. The permanent answer for any site that publishes on a calendar is a five-minute system cron, not hoping a visitor turns up at 9:00 sharp.

Leave a Reply to This Post