SKR Tips logo SKR TipsWebsite tips that actually work
WordPress

How to Fix WP-Cron When Scheduled Posts Miss on a Fan Site

You schedule the patch notes summary for 03:00, right when the servers come back from maintenance. You wake up at 08:30 and the post still says "Missed schedule" in red.

A WordPress dashboard showing a scheduled post with a missed schedule warning beside a wall clock

You schedule the patch notes summary for 03:00, right when the servers come back from maintenance. You wake up at 08:30 and the post still says "Missed schedule" in red. Discord already has three threads asking where the notes are, and someone linked a rival wiki instead. Nothing crashed. WordPress simply never got around to publishing.

The culprit is almost always WP-Cron, the system WordPress uses for anything timed: scheduled posts, backup plugins, newsletter sends, cache clearing. I ran a clan site and a fan wiki for years before I figured out how it really works, and the fix took me about fifteen minutes once I understood it. This guide covers why it fails on small community sites, how to check what is stuck, and how to move it to a real timer.

Why WP-Cron misses on quiet sites

WP-Cron is not a clock. It does not run in the background waiting for 03:00. Instead, every time someone loads a page, WordPress checks a list of due tasks stored in the database and, if anything is overdue, fires a quick request to itself at wp-cron.php to run them.

That design works fine on a site with steady traffic. It falls apart on a gaming fan site, where traffic comes in bursts. At 03:00 on a Tuesday your guild site might get zero visitors for two hours. No visitor means no page load, which means no check, which means the post sits there. When the first visitor does show up at 05:40, WordPress may publish then, or it may mark the post missed if a plugin or setting interferes with the loopback request.

A few other things break it even with traffic:

  • Full-page caching. If your cache plugin or host serves cached HTML, PHP never runs on those requests, so WordPress never checks the task list. I covered a related headache in the guide on caching plugins that hide your own updates.
  • Blocked loopback requests. Some security plugins and firewalls block the site from calling itself. Site Health will tell you: look for "Your site could not complete a loopback request".
  • Basic auth on staging or maintenance mode. A password prompt stops the self-request cold.
  • A stuck lock. WP-Cron sets a lock while it runs. If a long task dies halfway, the lock can block new runs for up to 60 seconds per attempt, and a plugin that keeps crashing will keep relocking it.

Check what is actually queued

Before changing anything, look at the queue. Install WP Crontrol, which is free and maintained by a long-time core contributor. Go to Tools, then Cron Events. You will see every scheduled task, its hook name, the next run time, and how often it repeats.

On a typical clan site with around 30 plugins I usually find 40 to 70 events. Look for three things. First, events with a next-run time in the past, shown in red or with "late" beside them. Second, duplicate events from the same plugin, sometimes dozens of copies, which happen when a plugin schedules itself on every page load by mistake. Third, events left behind by plugins you deleted months ago. Those orphans still fire, find no code to run, and waste a request each time.

Delete the orphans and the duplicates from the WP Crontrol screen. If you went through a plugin cleanup recently, this step finishes the job; my notes on removing unused plugins without leftovers explain why deactivating alone leaves this mess behind.

The common fix that only half works

Most tutorials say: install a "missed schedule" plugin and move on. These plugins check for overdue posts on each page load and force them out. They do help a little, but they rely on the exact same trigger that failed in the first place, a visitor arriving. On a quiet site at 03:00 they do nothing until someone shows up. You get your post at 05:40 instead of never, which is better, but not what you scheduled.

The other popular tip is to raise the PHP time limit so long tasks finish. That treats a symptom. If a backup plugin needs four minutes inside a visitor's page request, the real problem is that it is running inside a visitor's page request.

The proper fix is to stop relying on visitors at all and let the server call WP-Cron on a fixed timer.

Moving WP-Cron to a real server timer

This takes two changes, and you need both. Doing only the first one means nothing runs at all.

  1. Turn off the visitor trigger. Open wp-config.php and add this line above the comment that says "That's all, stop editing": define( 'DISABLE_WP_CRON', true );. WordPress will stop checking the queue on page loads.
  2. Add a server cron job. In cPanel, open Cron Jobs. Set it to run every 5 minutes and use this command, with your own domain: wget -q -O - https://yourguild.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1. Plesk calls this Scheduled Tasks; pick "Fetch a URL" and paste the address. If your host offers WP-CLI, its cron event run command with the due-now flag and the path to your install is cleaner, since it does not go through the web server.
  3. Pick the interval sensibly. Every 5 minutes suits most fan sites. Every minute is fine on a VPS but some shared hosts limit you to 15 minutes or flag frequent jobs as abuse. Check your host's rules first.
  4. Test it. Schedule a dummy draft for 10 minutes from now. Watch WP Crontrol. The publish event should leave the list within one interval of its time.
  5. Write it down. Add a note in your team's admin doc saying cron runs from the server. The next volunteer who migrates the site will otherwise forget the server job, and scheduled posts will silently stop on the new host.

If your host has no cron panel at all, which happens on some very cheap plans, a free external pinging service can hit the same URL every few minutes. It is less reliable, and I would treat it as a reason to look at a better host.

Heavy tasks: give them their own slot

Once cron runs from the server, you can see which tasks hog it. Backup plugins and image optimisers are the usual offenders. On one wiki I helped with, a nightly full backup took nine minutes and pushed the patch notes post behind it. The fix was to move the backup to 04:30 in the plugin's settings and keep it well clear of the times we schedule content. If your database has grown huge from logs and transients, cleaning it shortens these runs too; see the walkthrough on database cleanup for a busy gaming fan site.

Email is the other quiet victim. Plugins that queue newsletters or notification emails send them through cron. If those mails never arrive even after this fix, the problem is delivery, not timing.

FAQ

Is it safe to set DISABLE_WP_CRON to true?

Yes, as long as a server cron job calls wp-cron.php on a schedule. Without that job, nothing timed will run at all, including updates checks and backups.

How often should a server cron job call WP-Cron?

Every 5 minutes works for most community sites. Use every minute only if you schedule posts to the exact minute and your host allows it.

Why does my post say Missed schedule even with traffic?

Usually full-page caching, a blocked loopback request, or a crashing plugin holding the cron lock. Site Health and WP Crontrol will show which one.

Do I still need a missed schedule plugin after this?

No. With a server timer in place, those plugins add nothing and can be removed.

MO
Matthias Okeke

Matthias ran a clan website and a fan wiki for years before writing about them. He covers the sites gaming communities build, from guides to patch note pages.

More posts by Matthias

More in WordPress

01doorganico 084goad 3hot8 4465456 after5internprogram ahandfulofblessings ahmadpc ahomcybersecurity allodialresources annodex antiquesandperiodcollectibles aotc asianworldkashmir beedlectrics bloggertemplatefree bluefeatheragency bodyweightsolution campaign08blog carfixadvisor catstreetcollective cbstvsports cnfanspurchasing committeeof500yearsofdignityandresistance covertopsdjs cs-kaiyuangame cuestacomunicaciontotal cxoutlet dancebetgame deskgearhub destinyvillastay detectmagazine di33 diamondsteelbuildings donmo dyanadropship emarsysfinance emimattl escapechan eskitarih evautoplay everythingweddings-orlando farmzone feedviewerapp foxstudiosscratch gamevui123 gleasongrade greetingsday hacklabasu haskell iw-kaiyun jagoanproperti jilidskripsi joltbusinessfinance juegodepokergratis juegospucca kaeru-festival latestmehndidesigns lestanzediottaviano litemagazines localcommunityhero lustrsazan mariashebets menswheels newwebstechs1 nikkit ootheday oxigenvpn plurfitness prefetial probudgetcarpetcleaning psychicmrsmartin pyaten qvantor rejoignezlogis rexzbot riselayouts ruchin rwxqsgd5 sakaltime shibiaowang shop-bot strcoding tetapatrium topselller uneplanteartificielle vipertechcr webtech1223411 wo-kygaming yamaha-mekar yeknava yoybuysheets zenunwa yamaha-mekar jilidskripsi ankertoto ankertoto ankertoto ankertoto