In short: WordPress does not publish scheduled posts with a clock that runs all the time. It uses WP-Cron, which only checks for due tasks when someone loads a page. If nobody visits, if caching keeps requests away from WordPress, if the site cannot call itself (loopback) or if WP-Cron is switched off without a replacement, the post stays in the "Missed schedule" state. The reliable fix is a real server cron job that calls wp-cron.php every few minutes, combined with DISABLE_WP_CRON. Check the time zone under Settings > General as well.
What "Missed schedule" means
When you schedule a post, WordPress gives it the status future. In the posts list, WordPress shows "Scheduled" for such a post as long as its date is ahead, and "Missed schedule" in red once the date has passed and the post still has the status future. You can see this logic in the source of the posts list table (WP_Posts_List_Table::column_date (external link)).
So "Missed schedule" does not mean the post is broken. It means the task that should have published it has not run yet.
How WordPress publishes a scheduled post
When a post is scheduled, WordPress registers a one-time event with the hook publish_future_post and the post ID, timed to the post's date in GMT (_future_post_hook (external link)). When that event runs, WordPress checks that the post still has the status future and that its time has come, and then publishes it (check_and_publish_future_post (external link)).
The event is run by WP-Cron. The WordPress plugin handbook explains how WP-Cron differs from a real cron: "WP-Cron works by checking, on every page load, a list of scheduled tasks to see what needs to be run." And: "WP-Cron does not run constantly as the system cron does; it is only triggered on page load." The handbook gives an example: if a task is scheduled for 2:00 PM and no page loads happen until 5:00 PM, the task runs late (Plugin handbook: Cron (external link)).
On a page load, WordPress starts the cron run by sending a short request to its own wp-cron.php, which "doesn't halt page loading" (spawn_cron (external link)). This request from the site to itself is called a loopback. Site Health describes it the same way: "Loopback requests are used to run scheduled events" (Site Health screen (external link)).
That gives you three things that must work for a scheduled post to go live on time:
- A page load reaches WordPress around the scheduled time.
- WordPress can send the loopback request to
wp-cron.php. - The cron run completes without errors.
Common causes
| Cause | What happens | Where to check |
|---|---|---|
| Low traffic | Nobody loads a page, so WP-Cron is not triggered (Plugin handbook (external link)). | Does the post publish as soon as you open the site? |
DISABLE_WP_CRON without a real cron | WP-Cron no longer starts on page loads, and nothing else calls it (wp-config.php docs (external link)). | wp-config.php; wp cron test reports an error (WP-CLI (external link)). |
| Full-page caching or a CDN | WP-Cron is triggered on page load in WordPress. If a cache answers the request without starting WordPress, that visit does not trigger it. | Cache and CDN settings of your host or plugin. |
| Loopback request blocked | The request to wp-cron.php fails, so due events do not run (WP_Site_Health::can_perform_loopback (external link)). | Tools > Site Health > Status. |
| Firewall, security plugin or password protection | A rule blocks requests to wp-cron.php or from the server itself. The cron request carries no login data (spawn_cron (external link)). | Firewall logs, security plugin settings, server password protection. |
| Wrong time zone | WordPress converts the post date from the site time zone to GMT (_future_post_hook (external link)). A wrong setting moves the real publishing time. | Settings > General > Timezone. |
| Server cron set up wrong | The cron line uses a wrong URL, a wrong path or never runs. | crontab -l, hosting panel, server log. |
| PHP errors | A fatal error during the cron run can stop it before the post is published. | wp-content/debug.log (Debugging in WordPress (external link)). |
Diagnose it step by step
Work through the steps in this order. Most cases are solved by step 3.
Step 1: check the time zone
Go to Settings > General > Timezone. The WordPress documentation recommends choosing "a city in the same timezone as you", for example New York for the US Eastern time zone with daylight saving time. After saving, WordPress shows UTC time and local time so you can confirm the setting (Settings General screen (external link)). A city follows daylight saving time; a fixed UTC offset does not.
Then open the scheduled post and compare its date with the local time shown on that screen.
Step 2: run Site Health
Go to Tools > Site Health (Site Health screen (external link)). Look at the Status tab for these results:
- "Your site could not complete a loopback request": WordPress cannot call itself, so scheduled events cannot start.
- "A scheduled event has failed" or "A scheduled event is late": WP-Cron is not running events on time (WP_Site_Health::get_test_scheduled_events (external link)).
In the Info tab, the section "WordPress Constants" shows constants from your configuration, and the WordPress section shows the time zone.
One limit: the Site Health loopback test passes on HTTP login data if the browser sent it (can_perform_loopback (external link)), while the normal cron request does not send any (spawn_cron (external link)). On a staging site behind a server password, the test can pass while WP-Cron still fails.
Step 3: check WP-Cron with WP-CLI
If you have SSH access and WP-CLI (external link), run these commands in the WordPress folder.
Test whether WP-Cron can start:
wp cron test
If everything works, the output is Success: WP-Cron spawning is working as expected. The command reports an error if DISABLE_WP_CRON is set and warns if the request does not return status 200 (wp cron test (external link)).
List the scheduled events and look for publish_future_post:
wp cron event list --fields=hook,next_run_gmt,next_run_relative,args
The default fields are hook, next_run_gmt, next_run_relative and recurrence; args is one of the optional fields (wp cron event list (external link)). An event for your post that is listed in the past means WP-Cron has not run since that time.
Run all due events by hand:
wp cron event run --due-now
This "Run[s] all hooks due right now" (wp cron event run (external link)). If the post is published afterwards, publishing itself works and the problem is only the trigger. If the command shows an error, look at the PHP error log next.
Step 4: look for PHP errors
Turn on logging in wp-config.php above the line "That's all, stop editing!" (Debugging in WordPress (external link)):
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
@ini_set( 'display_errors', 0 );
The log is written to debug.log in the content folder, usually wp-content/debug.log. Run wp cron event run --due-now again and read the log. Switch debug mode off again when you are done, because Site Health warns when the log file may be public (Site Health screen (external link)).
Fix 1: use a real server cron (recommended)
This makes publishing independent of visitors. The plugin handbook describes it in two parts (Hooking WP-Cron into the system task scheduler (external link)).
Part 1: stop WP-Cron on page loads. Add this line to wp-config.php:
define( 'DISABLE_WP_CRON', true );
Place it above the line "That's all, stop editing!" and above any require_once (wp-config.php docs (external link)).
Part 2: call wp-cron.php from the system cron. Open the crontab with crontab -e and add a line. The handbook uses */15 * * * * for every 15 minutes and wget --delete-after for the request; without --delete-after, wget would save the response as a file:
*/15 * * * * wget --delete-after https://www.example.com/wp-cron.php
Replace www.example.com with your domain. The handbook leaves the interval to you. For posts scheduled to the minute, a shorter interval such as every 5 minutes means a post goes live at most about 5 minutes late:
*/5 * * * * wget -q --delete-after https://www.example.com/wp-cron.php
If WP-CLI is installed on the server, you can let it run due events directly instead of going through HTTP (wp cron event run (external link)):
*/5 * * * * /usr/local/bin/wp --path=/var/www/example cron event run --due-now --quiet
Use your real WordPress path. Many hosting panels offer a form for cron jobs instead of crontab -e; the line you enter is the same command.
Do both parts together. DISABLE_WP_CRON on its own turns scheduling off completely, which is one of the causes above.
After the change, check it:
wp cron event list --fields=hook,next_run_relative
No event should stay in the past for longer than your cron interval.
Fix 2: repair the loopback request
If you want to keep WP-Cron on page loads, the site must be able to reach itself:
- Firewall and security plugins: allow requests to
/wp-cron.php, including requests from your own server. - Password-protected staging sites: the cron request sends no login data (spawn_cron (external link)). Exclude
wp-cron.phpfrom the protection or use a server cron with WP-CLI as in fix 1. - PHP sessions: Site Health warns that a session started with
session_start()"interferes with REST API and loopback requests" (Site Health screen (external link)). Find the plugin that starts it. - Caching: if you keep WP-Cron on page loads, make sure enough requests actually reach WordPress. A server cron (fix 1) removes this dependency.
Run Tools > Site Health again afterwards. It should show "Your site can perform loopback requests".
WordPress also has ALTERNATE_WP_CRON. The documentation says it is used "if scheduled posts are not getting published as predicted": the visitor's browser is redirected while cron keeps running in the dropped connection, and the docs add that "This method has certain risks" (wp-config.php docs (external link)). Treat it as a last resort when you cannot set up a server cron.
Fix 3: correct the time zone
Set a city under Settings > General > Timezone and save (Settings General screen (external link)). Then open posts that were scheduled before the change and check their dates. If a post shows "Missed schedule" only because the time zone was wrong, publish it by hand or set a new date.
Copyable checklist
[ ] Settings > General > Timezone: a city, local time correct
[ ] Tools > Site Health: no loopback error, no late or failed scheduled event
[ ] wp cron test: Success (only if WP-Cron still runs on page loads; with DISABLE_WP_CRON it reports an error by design)
[ ] wp cron event list: no publish_future_post event in the past
[ ] wp cron event run --due-now: post publishes, no error
[ ] wp-config.php: DISABLE_WP_CRON true only if a server cron runs
[ ] crontab -l: line calls wp-cron.php (or wp cron event run --due-now) every 5-15 min
[ ] Firewall, security plugin, password protection: wp-cron.php allowed
[ ] wp-content/debug.log: no fatal errors during the cron run
Scheduling with Blog Automation
In Blog Automation you schedule approved posts in the content calendar. It shows scheduled, published and failed posts, and each connected website has its own time zone setting, so check that it matches your WordPress time zone. If publishing fails, the dashboard lists the post under items that need attention, with the WordPress error text, and an e-mail notification tells you about the failed post. How the connection works, with or without the plugin, is described on the WordPress page. For the whole workflow from idea to published post, read how to automate blog posts in WordPress.
Frequently asked questions
Why does my scheduled post publish only when I open the site?
Because WP-Cron is triggered by page loads (Plugin handbook: Cron (external link)). Your own visit starts the overdue task. On a site with few visitors, use a server cron as described in fix 1.
How do I publish a post that already shows "Missed schedule"?
Open the post and click Publish, or run wp cron event run --due-now with WP-CLI (wp cron event run (external link)). That publishes the post (the command runs every event that is due) but does not fix the cause, so work through the diagnosis afterwards.
Is it safe to set DISABLE_WP_CRON to true?
Only together with a server cron. The constant turns off WP-Cron on page loads (wp-config.php docs (external link)). Without a replacement, scheduled posts, update checks and other scheduled tasks no longer run on their own.
Does "Missed schedule" affect other things than posts?
The same system runs other tasks. Site Health describes scheduled events as "what periodically looks for updates to plugins, themes and WordPress itself" (WP_Site_Health::get_test_scheduled_events (external link)). If posts miss their schedule, these tasks are probably late too.
How often should the server cron run?
The WordPress handbook does not prescribe an interval (Hooking WP-Cron into the system task scheduler (external link)). Pick it by how exact your publishing times must be: every 5 minutes means a post goes live at most about 5 minutes after its time, every 15 minutes at most about 15.
