WordPress Missed Schedule: Why WP-Cron Skipped Your Post

Line illustration of a clock face, representing a scheduled WordPress job that did not run on time

What the missed schedule error is telling you

Missed schedule means WordPress never ran the job that was supposed to publish your post. The post itself is fine: it still exists, it still has the status future, and it will go out the moment that job finally runs, which is why the fix is never in the post and always in the scheduler behind it.

This one is for people who write ahead. A blog queued on Friday for Monday morning, a sale page timed to nine o’clock, a client site where somebody else will notice before you do. The mechanism is the same in all three cases, and so is the repair.

The red text comes from one comparison

WordPress prints those two words when a post still carries the status future and its publish time has already gone past. The posts list runs the check itself: if 'future' === $post->post_status and the time difference is greater than zero, the column shows Missed schedule instead of Scheduled, as you can see in the source of the date column.

So it is not a diagnosis. It is WordPress comparing two numbers and telling you the second one is late. Nothing inside the post is damaged, which is also why opening the post and clicking update often appears to fix it: changing a future post schedules the publish job again, and this time something runs it.

WP-Cron is waiting for a visitor

WordPress has a scheduler, but it is not a scheduler in the sense a server administrator means. The handbook is blunt about it: WP-Cron “works by checking, on every page load, a list of scheduled tasks to see what needs to be run”, and it “does not run constantly as the system cron does; it is only triggered on page load”.

The example the handbook gives is the exact situation you are in: “Scheduling errors could occur if you schedule a task for 2:00PM and no page loads occur until 5:00PM.” Your post was never forgotten. Nobody came to the door, so nobody checked the list.

Low traffic is the most common reason of all

A site with steady traffic almost never sees this, because there is always somebody triggering the check within a minute or two of the scheduled time. A site with forty visitors a day has long gaps, and the gaps get longer at exactly the hours people schedule posts for: early morning, late evening, weekends.

Worth knowing before you go plugin shopping, because a quiet site is not broken. It is a site where the publishing mechanism depends on an audience that has not arrived yet, which is a design detail rather than a fault.

Caching means fewer PHP requests than your traffic suggests

Here is the version that catches busier sites. Full page caching, whether it lives in a plugin, at the host, or on a CDN edge, answers most visitors with stored HTML. WordPress never boots for those requests, so nothing checks the schedule. The better your cache hit rate, the quieter your scheduler gets.

That produces a genuinely confusing symptom: traffic is healthy, the dashboard looks normal, and scheduled work still runs late. If you tightened caching recently and posts started missing their slot afterward, those two events are the same event. This is one of the reasons performance work and maintenance work belong in the same conversation rather than in different quarters.

The sixty second lock, and the job that holds it

WordPress will not let two cron runs overlap. It sets a doing_cron transient when a run starts, and the comment in core explains the rule plainly: “Don’t run if another process is currently running it or more than once every 60 sec.” That window is WP_CRON_LOCK_TIMEOUT, which defaults to 60 seconds, and the logic lives in spawn_cron().

Most of the time the lock is invisible. It stops being invisible when one event is slow. An import that walks ten thousand rows, or a plugin calling an external API with no timeout set, can hold the run while everything behind it waits, including the job that was meant to publish your post at nine.

Check the clock before you check anything else

WordPress stores two dates on every post: the local one shown in the editor and the UTC one it schedules against. If the timezone in Settings then General is wrong, or was changed after the post was queued, the post publishes at the right moment in the wrong time zone and everyone concludes the scheduler is broken.

Quick test. Open Settings then General and read the current local time WordPress prints there. If that clock disagrees with yours, you do not have a cron problem yet. You have a clock problem, and it will look exactly like a cron problem until you fix it.

Fixing it so it stays fixed

There are two real repairs here and a lot of advice that is neither. The first repair is finding out whether the event still exists. The second is giving WordPress a scheduler that does not depend on visitors.

Look at the queue before changing anything

If you have WP-CLI on the server, wp cron event list prints every scheduled event with its next run time. Sort your eyes down the list for publish_future_post. Two possible findings, and they lead in opposite directions.

If the event is there with a time in the past, the queue is fine and nothing is running it, so read the next two sections. If the event is not there at all, the schedule was lost rather than missed, usually by an import, a migration, or a plugin that cleared the cron array. Opening the post and saving it again puts the event back. Running wp cron event run --due-now clears everything that is overdue in one go and is the fastest way to confirm which of the two you have.

Turn WP-Cron off and drive it from real cron

This is the fix that holds. Add one line to wp-config.php, above the line that tells you to stop editing:

define( 'DISABLE_WP_CRON', true );

Then have the server call the scheduler on a fixed interval. The handbook’s own example for hooking WP-Cron into the system task scheduler is a daily one, 0 0 * * * wget --delete-after http://YOUR_SITE_URL/wp-cron.php, which is right for the general case and much too slow for publishing. Every five minutes is the interval that matters when a post has a slot to hit.

One warning that is easy to skip. If you add the crontab entry and forget the constant, both mechanisms run, and you have made the site busier rather than more reliable. The constant is not optional.

The request has to reach PHP, not a login prompt

A server cron that returns a 401 is a server cron that does nothing. Three things break this quietly: HTTP authentication on a staging or pre-launch site, a firewall or bot rule that treats a command line user agent as a crawler, and a redirect from the URL you put in the crontab to the canonical one.

Where you can, skip HTTP entirely and run it through WP-CLI instead: */5 * * * * cd /path/to/site && wp cron event run --due-now. That version never touches the web server, so authentication, caching rules and loopback problems stop being able to break it. If you are on shared WordPress hosting without shell access, the wget or curl version is the one available to you, and it works as long as the URL it calls returns a 200.

Check the loopback before blaming the schedule

Open Tools then Site Health. WordPress runs its own tests there, and two of them are exactly this subject: whether scheduled events have failed, and whether the site can complete a loopback request to itself. A failed loopback means WordPress cannot call its own wp-cron.php even when a visitor does arrive, which is a hosting or DNS condition rather than a WordPress setting, and no plugin will talk it round.

Site Health is also where you find out the problem is older than you thought. The scheduled events test does not only report the post you were looking for. It reports everything that is overdue, which is usually a longer list than expected.

The republish plugins fix one symptom

There are plugins whose whole job is to look for posts stuck at future and publish them. They work, and on a small blog where the only scheduled thing that matters is a post, that may genuinely be enough.

Understand what you are buying, though. The plugin is a second scheduler running on the same broken foundation, and it only watches posts. Everything else in the queue is still late. If the reason you are here is a missed post, the reason you should stay is the rest of the list.

What else was running late while you were looking at the post

Backups. Update checks. Sitemap regeneration. Session and transient cleanup. Retries on transactional email. Abandoned cart mail in a store. Subscription renewals. None of these announce themselves, and all of them sit in the same queue as the post that told on the scheduler by going red in an admin screen.

Email is the one that hurts most, because it fails without leaving a trace on the site. If your forms and receipts go out through the server’s default mailer, a late or dead cron queue and a broken mail path look identical from the outside: nothing arrives and nothing is logged. Sending through a real delivery path, whether that is a relay you already pay for or our Email Delivery for Cloudflare plugin, at least puts the failures somewhere you can read them. The wider version of that argument is on the transactional email page.

We made the same point from the other direction when writing about what maintenance mode does not protect you from: the parts of a WordPress site with no browser attached are the parts nobody is watching.

Where this lands once somebody owns it

A scheduler that depends on traffic is fine until the day it is not, and the day it is not tends to be the day something was timed to matter. The durable version is unglamorous: the constant set, a real cron entry at five minute intervals, and something that notices when the queue stops draining rather than when a customer does.

That last part is the one people skip, and it is the substance of managed WordPress maintenance. We run WordPress plugin updates, theme updates, and core updates on a consistent schedule, back up your site every 24 hours with 180 days of cloud retention, scan for malware every 24 hours, and monitor uptime 24/7, with site error monitoring and daily form submission testing alongside it. If you want to see how that compares with running it yourself or with a dashboard tool, the maintenance comparisons set it out. Sites with heavier custom work usually need a closer look instead, because a cron queue that jams every week is normally one plugin’s fault and worth finding.

Ask Us Anything
We’d love to hear from you!
Contact Form