Missed schedule: WordPress geplande berichten alsnog gepubliceerd krijgen

WordPress toont 'Missed schedule' wanneer de publicatietijd voorbij is maar de cron-runner nooit afging. Het bericht is niet weg. Dit artikel zet het vastgelopen bericht binnen een minuut live en lost de oorzaak zo op dat de volgende keer wel gewoon op tijd publiceert.

Je had een bericht ingepland voor 09:00. Het is nu 10:30, het bericht staat nog niet op de voorkant en in de Berichten-lijst zie je een rood label "Gemiste planning" (of "Missed schedule") in plaats van "Gepland". Soms staat de datum nog op de oorspronkelijke publicatietijd. Soms staat er "Gemiste planning op 09:00" terwijl die tijd al uren voorbij is.

Wat "Missed schedule" eigenlijk betekent

WordPress is je bericht niet kwijt. De inhoud staat er gewoon. Wat faalde, is de runner die het bericht op het geplande moment van future naar publish had moeten zetten.

Op het moment dat je in de editor op "Plannen" klikt, slaat WordPress het bericht op met post_status = future en registreert hij een eenmalig cron-event genaamd publish_future_post voor exact die timestamp. Dat event wordt aan jouw bericht gekoppeld via _future_post_hook(), die op zijn beurt wp_schedule_single_event() aanroept om het publicatiemoment vast te leggen. Als dat moment aanbreekt en de cron-runner afgaat, roept check_and_publish_future_post() (zit in core sinds WordPress 2.5.0) wp_publish_post() aan, die het bericht overzet naar publish en het gaat live.

Loopt die hele keten niet op tijd, dan blijft het bericht gewoon op future staan. De Berichten-lijst in wp-admin ziet dat de GMT-publicatietijd in het verleden ligt terwijl de status nog future is, en plakt er als UI-signaal het rode "Missed schedule" label op. Er is geen aparte "missed_schedule" status in de database. Het is dezelfde future status, alleen is de wandklok de geplande tijd voorbij.

De onderliggende oorzaak is bijna altijd dat WP-Cron niet liep toen het moest. WP-Cron is een PHP-routine die op page loads draait, niet een echte cron-daemon, en heeft een paar bekende redenen om zijn venster te missen.

Oorzaken op volgorde van waarschijnlijkheid

Na honderden van deze meldingen op managed WordPress-sites is de volgorde steeds dezelfde. De eerste drie dekken het overgrote deel.

  1. Geen verkeer op het publicatiemoment. WP-Cron draait alleen wanneer een bezoeker (mens of bot) een niet-gecachet PHP-verzoek doet. Een bericht dat op een rustig blog gepland staat voor 03:00 op zondag blijft gewoon op future staan tot er weer een bezoeker langskomt.
  2. Agressieve full-page caching die de trigger opvangt. Een reverse-proxy cache, nginx fastcgi_cache, LSCache, WP Rocket disk cache of Cloudflare Cache Reserve serveert verzoeken zonder dat PHP überhaupt opstart. WordPress wordt niet gebootstrapt en spawn_cron() draait dus niet. De analytics ogen druk terwijl WP-Cron stilletjes stilstaat.
  3. DISABLE_WP_CRON staat in wp-config.php zonder werkende externe runner. Veel managed hosts (WP Engine, Kinsta, Cloudways, SpinupWP) leveren standaard define( 'DISABLE_WP_CRON', true ); met hun eigen runner ervoor in de plaats. Als de site is gemigreerd, de runner is verwijderd, of iemand WP-Cron heeft uitgezet "voor de performance" zonder een system cron toe te voegen, dan stopt elk ingepland event in stilte.
  4. Timezone-mismatch tussen WordPress en de server. WordPress slaat geplande tijden op in twee kolommen: post_date (in de display-timezone van je site) en post_date_gmt (in UTC). De cron-runner vergelijkt post_date_gmt met de huidige GMT-tijd van de server. Als een plugin of thema date_default_timezone_set() op een waarde anders dan UTC zet, dan wordt de rekensom dubbelop of net weer ongedaan gemaakt en komen posts op het verkeerde moment in het verleden of ver in de toekomst terecht.
  5. De PHP memory limit werd geraakt tijdens de cron-run. Een zware plugin die binnen het wp-cron.php verzoek wordt geladen kan over de PHP memory limit heen schieten. De cron-run klapt dan met Allowed memory size exhausted voordat hij ooit bij check_and_publish_future_post() aankomt. Het bericht blijft op future staan.
  6. Een plugin gooit een fatal in de publish-hook. Een plugin die in transition_post_status, publish_post of een van de save-actions hangt, kan tijdens het publiceren fataal crashen. De cron-run breekt af. De status wordt niet overgezet. WordPress laat "There has been a critical error" alleen zien als je handmatig publiceert; de cron-route slikt het zonder zichtbare melding.

In de diagnose-sectie hieronder zie je welke van toepassing is voor jouw site, voordat je dingen gaat aanpassen.

Snelle redding: dit bericht nu live krijgen

Voordat je gaat debuggen, eerst het vastgelopen bericht live krijgen. Kies de route waarvoor je toegang hebt.

Optie A: opnieuw triggeren vanuit wp-admin (geen plugin, geen shell)

  1. Open de Berichten-lijst in wp-admin en zoek het bericht met het rode "Gemiste planning" label.
  2. Klik op Snel bewerken.
  3. Wijzig de Datum naar een moment in het verleden (een minuut geleden is genoeg), of zet de Status direct op Gepubliceerd.
  4. Klik op Bijwerken.

Het bericht is binnen een seconde live. Dit werkt omdat opslaan met een datum in het verleden plus status publish het direct via wp_publish_post() overzet, en het cron-event wordt overgeslagen. Je weet dat het werkt zodra het rode label uit de Berichten-lijst verdwijnt en het bericht op de voorkant verschijnt.

Optie B: Missed Scheduled Posts Publisher installeren (vangt ook toekomstige gevallen op)

Als je geen shell-toegang hebt en alvast een vangnet wilt terwijl je de echte oorzaak aanpakt, installeer dan Missed Scheduled Posts Publisher van WPBeginner (versie 2.1.1, 60.000+ actieve installaties). De plugin registreert een eigen WP-Cron event dat elke vijftien minuten draait, kijkt of er future-berichten zijn waarvan de publicatietijd al voorbij is, en publiceert ze alsnog.

Wees eerlijk over wat deze plugin is. Hij behandelt het symptoom, niet de oorzaak. Hij is zelf afhankelijk van WP-Cron dat überhaupt loopt, en zijn 15-minuten interval betekent dat een bericht gepland voor 09:00 mogelijk pas om 09:15 verschijnt. Is de echte oorzaak DISABLE_WP_CRON zonder runner, dan helpt deze plugin ook niet, want zijn eigen event draait om dezelfde reden niet als jouw publicatie. Zie het als noodverband, niet als oplossing.

Optie C: WP-CLI (als je SSH-toegang hebt)

Vanuit de site-root over SSH:

# Draai elk cron-event dat overdue is, inclusief publish_future_post
wp cron event run --due-now

Het commando omzeilt HTTP volledig, draait de WordPress bootstrap in PHP CLI context en vuurt elk overdue event af. Was het enige probleem dat WP-Cron simpelweg nog niet getriggerd was, dan staat de gemiste post na dit commando direct online. De volledige reference staat in de WP-CLI cron docs.

Wil je het publicatie-event voor een specifiek bericht aanroepen:

# Vind het post-ID
wp post list --post_status=future --fields=ID,post_title,post_date

# Trigger publish_future_post voor dat ID
wp cron event run publish_future_post

Je weet dat het werkt zodra wp post list --post_status=future het bericht niet meer toont en wp post get <ID> --field=post_status antwoordt met publish.

Diagnose: welke oorzaak speelt hier?

Zodra het vastgelopen bericht live staat, achterhaal je waarom het bleef hangen, zodat het volgende bericht het wel haalt.

Stap 1: check Site-status op gemiste cron-events

Open in wp-admin Gereedschap, dan Site-status, dan Status. WordPress draait een check op "Geplande evenementen" die elk geregistreerd cron-event langsloopt en degene markeert waarvan de geplande tijd voorbij is met meer dan een hard ingebouwde drempel: standaard 5 minuten, opgerekt tot 1 uur als DISABLE_WP_CRON op true staat. Toont Site-status een aanbeveling met de tekst "Een geplande gebeurtenis is mislukt" of "Een geplande gebeurtenis is laat", dan heb je directe bevestiging dat de runner niet loopt.

Waar je op moet letten:

  • "Een geplande gebeurtenis is mislukt" met publish_future_post in de lijst van vastgelopen events
  • "Een geplande gebeurtenis is laat"
  • Een gefaalde loopback-test (onder "Info, dan Geplande evenementen"), wat betekent dat spawn_cron() wp-cron.php niet vanaf de server naar zichzelf kan bereiken

Stap 2: check de timezone-instellingen

Ga in wp-admin naar Instellingen, dan Algemeen. Noteer de Tijdzone (bijvoorbeeld "Amsterdam" of "UTC+1"). De wp_timezone() functie van WordPress, geïntroduceerd in WordPress 5.3.0, leest die instelling en wordt door de cron-pipeline gebruikt om geplande tijden naar UTC te vertalen.

Vergelijk dat met de PHP-timezone op de server. Voeg tijdelijk een diagnose-bestand toe aan de site-root genaamd tz-check.php:

<?php
echo 'PHP date_default_timezone_get(): ' . date_default_timezone_get() . "\n";
echo 'PHP date(): ' . date('Y-m-d H:i:s T') . "\n";

Bezoek https://jouwsite.nl/tz-check.php één keer. Verwachte output op een gezonde site:

PHP date_default_timezone_get(): UTC
PHP date(): 2026-04-24 13:42:11 UTC

Verwijder het bestand zodra je klaar bent. Staat de PHP-timezone op iets anders dan UTC, dan heeft een plugin of thema ergens date_default_timezone_set() aangeroepen, en alleen dat is genoeg om geplande posts op het verkeerde moment te laten publiceren. WP Crontrol noemt dit in de PHP default timezone note als hoofdoorzaak en raadt aan de aanroep op te sporen en te verwijderen.

Stap 3: check of WP-Cron bereikbaar is

Heb je alleen wp-admin, dan is de Site-status "Geplande evenementen" Info-entry je antwoord.

Heb je SSH:

# Dit is de definitieve check
wp cron test

wp cron test geeft een eenregelige pass of fail terug. Bij een fail print hij de exacte cURL-error die de loopback raakte. Die mappen 1-op-1 op de WP Crontrol cURL error tabel (cURL 6 is DNS, cURL 7 is connection refused, cURL 28 is timeout, cURL 35 is TLS handshake, HTTP 401/403 is BasicAuth of een security-plugin, HTTP 404 betekent dat wp-cron.php mist of geblokkeerd is).

Stap 4: check op DISABLE_WP_CRON

Open wp-config.php via de bestandsbeheerder van je hostingpanel (of haal het op via SFTP) en zoek op de string DISABLE_WP_CRON. Vind je:

define( 'DISABLE_WP_CRON', true );

dan staat WP-Cron bewust uit en moet iets anders de events draaien. Vraag je hostingprovider of zij een managed cronjob voor je site draaien en op welk interval. Is het antwoord "nee" of "geen idee", dan komen de gemiste planningen daar vandaan: er is gewoon niets dat WP-Cron triggert.

Stap 5: check het debug-log op fatals tijdens cron

Schakel het WordPress debug-log in (bewerk wp-config.php via de bestandsbeheerder en voeg define( 'WP_DEBUG', true ); en define( 'WP_DEBUG_LOG', true ); toe). Wacht tot de eerstvolgende gemiste planning. Open daarna wp-content/debug.log via de bestandsbeheerder. Zoek naar regels rond het verwachte publicatiemoment met Allowed memory size, Fatal error, Uncaught Error, of een plugin-pad in een stack trace. Een fatal in de cron-context komt hier naar voren, ook als de voorkant nooit een fout liet zien.

Oorzaak-specifieke fixes

Fix 1: weinig verkeer of full-page caching

Vervang WP-Cron door een system cron die elke minuut afgaat, ongeacht bezoekers. Twee paden, afhankelijk van je toegang.

cPanel-achtige hostingpanels (geen SSH). Open in cPanel Cron Jobs, zet de schedule op "Every minute" (of "Once per five minutes" als je dat lichter vindt), en gebruik:

wget -q -O /dev/null "https://jouwsite.nl/wp-cron.php?doing_wp_cron" > /dev/null 2>&1

Hosting met SSH en WP-CLI. Open de crontab van de site-user met crontab -e en voeg toe:

* * * * * /usr/local/bin/wp cron event run --due-now --path=/var/www/jouwsite.nl/htdocs --quiet > /dev/null 2>&1

Voeg vervolgens aan wp-config.php (via de bestandsbeheerder of SFTP) toe, boven de regel "/* That's all, stop editing! */":

// Zet de page-load trigger uit; de system cron drijft WP-Cron nu aan.
define( 'DISABLE_WP_CRON', true );

De volledige redenering, inclusief waarom elke minuut en waarom WP-CLI beter werkt dan wget, staat in WordPress wp-cron: waarom het misgaat en hoe je het vervangt.

Je weet dat het werkt zodra je een testbericht inplant voor twee minuten later, even weggaat, en dat bericht op de verwachte tijd gewoon op de voorkant staat.

Fix 2: DISABLE_WP_CRON zonder runner

Drie opties:

  • Voeg een system cron toe zoals in Fix 1. Dit is de juiste oplossing voor een self-hosted setup.
  • Zet WP-Cron weer aan door de regel define( 'DISABLE_WP_CRON', true ); uit wp-config.php te halen. Alleen verstandig als je verkeer constant genoeg is om WP-Cron op page loads betrouwbaar af te laten gaan.
  • Vraag je managed host of hun ingebouwde cron-runner gezond is. WP Engine, Kinsta, Cloudways en SpinupWP draaien allemaal hun eigen variant. Is die kapot, open een ticket. Zet er niet zomaar een tweede cron bovenop zonder af te stemmen: twee runners die met elkaar vechten over dezelfde events veroorzaakt lock contention.

Je weet dat het werkt zodra wp cron event list --due-now (of de WP Crontrol events-tabel) twee minuten na een verwachte job geen overdue events meer toont.

Fix 3: timezone-mismatch

Als tz-check.php een non-UTC PHP-timezone liet zien, zoek de aanroep en haal hem weg. Doorzoek de codebase:

grep -rn "date_default_timezone_set" wp-content/

Veelvoorkomende boosdoeners zijn oude caching-plugins, custom themes met een "fix timezone" snippet uit een tutorial uit 2014, en een paar verouderde date-handling libraries. Verwijder de aanroep (of update de plugin naar een actuele versie), en draai tz-check.php opnieuw. Verwachte output: UTC.

Staat de Instellingen, dan Algemeen, dan Tijdzone waarde in WordPress verkeerd (bijvoorbeeld "UTC" terwijl de site eigenlijk Europe/Amsterdam moet zijn), corrigeer dat. Toekomstige planningen gebruiken dan de nieuwe timezone. Bestaande geplande berichten hebben hun post_date_gmt al berekend tegen de oude waarde, dus plan die met de hand opnieuw via de editor.

Je weet dat het werkt zodra een vers testbericht gepland voor twee minuten later ook echt binnen die minuut publiceert.

Fix 4: PHP memory limit geraakt tijdens cron

Verhoog de limiet zodat de cron-run af kan ronden. Bewerk wp-config.php via de bestandsbeheerder en voeg toe, boven de regel "/* That's all, stop editing! */":

define( 'WP_MEMORY_LIMIT', '256M' );
define( 'WP_MAX_MEMORY_LIMIT', '512M' );

WP_MEMORY_LIMIT regelt de voorkant, WP_MAX_MEMORY_LIMIT dekt de admin- en cron-context. Sommige hosts cappen die op de PHP memory_limit uit php.ini, die je dan via de PHP-instellingen van je hostingpanel mee moet ophogen. De volledige troubleshooting voor memory-fatals staat in Fatal error: Allowed memory size of N bytes exhausted.

Je weet dat het werkt zodra het volgende geplande bericht op tijd publiceert en het debug-log rond het publicatiemoment geen Allowed memory size regels meer toont.

Fix 5: plugin-fatal in de publish-hook

Identificeer de plugin uit de stack trace in debug.log. Deactiveer hem dan vanuit wp-admin, plan een testbericht in en bevestig dat publiceren werkt. Heb je de plugin echt nodig, contact dan de support van die plugin met de stack trace en je PHP-versie. Is hij vervangbaar, vervang hem. Laat een plugin die tijdens cron crasht niet aan staan: hij breekt niet alleen geplande berichten, maar elke door cron aangedreven feature op de site (backup-runs, sitemap-rebuilds, WooCommerce-bestelmails).

Wat "Missed schedule" niet is

Drie dingen krijgen vaak ten onrechte de schuld voor gemiste planningen.

  • Het is geen teken dat je server eruit lag. Een gemiste planning betekent dat de cron-runner niet op tijd vuurde. De webserver kan duizenden requests per minuut afhandelen terwijl publish_future_post in stilte in de wachtrij blijft staan. Was de site zelf onbereikbaar, dan zag je 502 Bad Gateway of 503 Service Unavailable in je monitoring, en niet "Missed schedule" op één enkel bericht.
  • Het is niet de WordPress-tijdzone op zich. Een verkeerd ingestelde Instellingen, dan Algemeen, dan Tijdzone verschuift wanneer berichten publiceren (een uur of twee verkeerd), maar veroorzaakt normaal niet dat een publish_future_post event volledig wordt overgeslagen. De combinatie van die WordPress-instelling plus een date_default_timezone_set() aanroep in PHP is wat de rekensom breekt. Je moet ze allebei nakijken.
  • Het is niet opgelost door alleen Missed Scheduled Posts Publisher. Die plugin checkt elke 15 minuten, maar leunt op een werkende WP-Cron. Ligt WP-Cron plat, dan ligt zijn check ook plat. Als vangnet bovenop een gezonde WP-Cron is hij prima, als vervanger van een echte fix niet.

Wanneer escaleren

Heb je de oorzaak-specifieke fix doorgevoerd en lopen berichten nog steeds tegen "Missed schedule" aan, verzamel dan dit voor je een ticket bij je host opent of mij vraagt mee te kijken:

  • WordPress-versie, PHP-versie, MySQL- of MariaDB-versie
  • Naam en versie van het actieve thema
  • Lijst van actieve plugins (export met wp plugin list --status=active --fields=name,version als je WP-CLI hebt, of een screenshot van het Plugins-scherm)
  • De output van wp cron test (of, zonder SSH, de tekst onder wp-admin Gereedschap, dan Site-status, dan Info, dan Geplande evenementen)
  • De output van wp cron event list --due-now (of een screenshot van het WP Crontrol "Cron Events" scherm gesorteerd op next-run tijd)
  • De relevante regels uit wp-content/debug.log rond het gemiste publicatiemoment
  • Naam en pakket van je hostingprovider, plus of DISABLE_WP_CRON in wp-config.php staat
  • Bevestiging van welke fixes je al geprobeerd hebt

Met dat pakket aan bewijs kan elke competente host of freelancer het probleem in één keer pakken in plaats van drie.

Voorkomen dat dit weer gebeurt

Heb je de boel werkend, hou hem dan ook werkend:

  • Draai een system cron, niet WP-Cron op page load. Ook op een site met verkeer is een system cron betrouwbaarder en haalt hij WP-Cron uit het TTFB-budget van je bezoekers.
  • Monitor publish_future_post één keer per maand. Open WP Crontrol of draai wp cron event list en check of er geen events in het verleden vastzitten.
  • Hou de WordPress-tijdzone op de tijdzone van je redactiekalender en de PHP-timezone op UTC. Laat geen plugin date_default_timezone_set() overschrijven. Heeft een plugin dat echt nodig, vervang de plugin.
  • Stapel geen runners. Eén betrouwbare cron-runner is gezonder dan twee die met elkaar vechten.
  • Test je publicatie-pipeline na elke host-migratie of grote WordPress-update. Plan een wegwerpconcept in voor twee minuten later en bevestig dat hij publiceert. Eén minuut werk en je vangt de stille breuk die DISABLE_WP_CRON-migraties en agressieve nieuwe caching-plugins introduceren.

Behandel je geplande berichten als een kritisch onderdeel van je pijplijn (nieuwsbrieven, productlanceringen, tijdgebonden campagnes), dan is een system cron plus een audit van vijf minuten per maand precies het niveau aandacht dat ze verdienen. Minder dan dat en je gokt erop dat je bezoekers je werk wel even publiceren, en dat is de gok die het rode "Missed schedule" label überhaupt produceerde.

Wil je dat dit niet steeds jouw probleem is?

Als storingen blijven terugkomen, is de 'fix' vaak consistent beheer: updates, backups en monitoring die niet versloffen.

Bekijk WordPress onderhoud

Doorzoek deze site

Begin met typen om te zoeken, of blader door de kennisbank en blog.