Auf produktiven Seiten erzeugt wp cron oft unerwartete Last, weil WordPress Aufgaben erst bei einem Seitenaufruf startet. Genau dadurch verzögern sich geplante Jobs, steigen die TTFB-Werte, und Hintergrundprozesse beeinflussen die Performance spürbar.
Zentrale Punkte
- Traffic-Abhängigkeit: Aufgaben starten unzuverlässig ohne echte Server-Zeitsteuerung.
- Mehr Last: `wp-cron.php` verursacht PHP- und DB-Overhead.
- Caching-Effekte: Proxies/CDNs verhindern Cron-Trigger.
- Skalierungsgrenzen: Viele Jobs blockieren Worker und Datenbank.
- Transparenz: Kaum Logging und schwierige Fehlersuche.
Was WP‑Cron wirklich tut und warum das zählt
WP‑Cron ist ein PHP‑basiertes Pseudo‑Cron, das WordPress bei Seitenaufrufen anstößt, um fällige Jobs zu prüfen und auszuführen. Dadurch hängt die Ausführung von geplanten Aufgaben direkt am Besucherverhalten, statt an der Uhrzeit des Betriebssystems zu hängen, was die Zuverlässigkeit einschränkt. Fällige Aufgaben wie Veröffentlichungen, Backups oder Syncs starten also nur, wenn Requests eintreffen, was auf produktiven Seiten eine riskante Koppelung darstellt. Unter Last erzeugen gleichzeitige Prüfungen und Trigger unnötigen Overhead in PHP und der Datenbank, der die Reaktionszeit erhöht. In Summe agiert WP‑Cron eher als Behelf als als belastbares Job‑System für produktive Anforderungen.
Abhängigkeit vom Traffic: warum Jobs verspätet oder zu oft laufen
Zu wenig Traffic führt dazu, dass geplante Tasks verspätet laufen, was etwa bei Sicherungen oder zeitgenauer Kommunikation kritisch wird. Sehr hoher Traffic wiederum triggert häufige Aufrufe von `wp-cron.php`, die PHP‑Worker und Datenbank belasten. Dieser Gegensatz macht produktive Seiten verwundbar, weil Aufgaben entweder hängen bleiben oder die Seite unter Last verlangsamen. Zusätzlich verschärfen parallel fällige Ereignisse Lastspitzen, die TTFB und Backend‑Reaktionszeiten erhöhen. Wer die Hintergründe tiefer verstehen will, findet in WP‑Cron verstehen gebündelte Grundlagen.
Vergleich: WP‑Cron vs. Server‑Cron im Alltag
Ein direkter Vergleich zeigt, warum echte System‑Cronjobs produktiven Anforderungen gerechter werden als das WordPress‑interne Konstrukt, das auf Besucherereignisse reagiert. Server‑Cronjobs laufen unabhängig von Aufrufen, was die Planbarkeit erhöht und Job‑Spitzen in ruhigere Zeiten verlegt. Zudem entkoppelt ein System‑Cron die Frontend‑Performance von Hintergrundaufgaben, wodurch TTFB‑Ausreißer seltener auftreten. Monitoring und Logging lassen sich auf Systemebene feiner steuern, was die Fehlersuche verkürzt und Ausfallzeiten reduziert. Die folgende Tabelle fasst die Unterschiede zusammen und hilft bei der Entscheidung.
| Kriterium | WP‑Cron | Server‑Cron |
|---|---|---|
| Trigger | Seitenaufruf‑basiert | System‑Zeitplan |
| Zuverlässigkeit | Schwankend bei wenig/viel Traffic | Konstant zum geplanten Zeitpunkt |
| Einfluss auf TTFB | Erhöhter Overhead | Entkoppelt vom Frontend |
| Skalierung | Begrenzt bei vielen Jobs | Mehr Kontrolle über Worker |
| Monitoring | Begrenzt in WordPress | Umfassend via System‑Tools |
| Einsatzgebiet | Kleine Seiten, Tests | Produktive Installationen |
Caching, Proxies und verpasste Ausführungen
Full‑Page‑Caching, Reverse‑Proxies und CDNs reduzieren die realen PHP‑Hits, wodurch WP‑Cron seltener oder gar nicht auslöst. Für Besucher wirkt die Seite schnell, aber im Hintergrund bleiben fällige Aufgaben ohne Trigger liegen, was geplante Veröffentlichungen oder E‑Mail‑Abläufe verzögert. Diese unsichtbare Entkopplung erzeugt ein Risiko, weil Prozesse scheinbar „funktionieren“, tatsächlich aber aufgeschoben werden. Ich plane daher cron‑kritische Jobs bewusst mit System‑Cron ein und setze deren Laufzeiten in verkehrsarme Zeitfenster. So bleibt die Cache‑Wirkung hoch und die Aufgaben laufen zuverlässig im Hintergrund.
Skalierungsgrenzen: viele Jobs, wenig Luft
Mit steigender Zahl an Plugins wächst die Menge geplanter Ereignisse und die Frequenz ihrer Ausführung. Einige Jobs laufen kurz und harmlos, andere blockieren länger und konkurrieren um dieselben PHP‑Worker, was Anfragen in Warteschlangen drängt. Gleichzeitig verschärfen datenbankintensive Aufgaben die Situation, wenn Indizes fehlen oder Abfragen zu breit gestaltet sind. Auf produktiven Seiten führt dieser Mix zu Lastspitzen, die ich ohne dedizierte Steuerung nur schwer entschärfe. Ab einem gewissen Volumen bleibt ein Wechsel auf System‑Cron der verlässlichere Weg, um Luft zu schaffen.
Überwachung und Diagnose: pragmatischer Workflow
Ich starte mit einem Blick auf die langsamsten Requests und prüfe, wie oft `wp-cron.php` auftaucht und welche Spitzen korrelieren. Danach kontrolliere ich, welche Cron‑Events registriert sind, wie oft sie laufen und ob einzelne Aufgaben regelmäßig ausufern. Server‑Logs und Query‑Analyse decken schnell auf, welche Jobs MySQL belasten und wie lange sie dauern. Auf dieser Basis kann ich Intervalle strecken, Jobs bündeln oder Probleme gezielt entfernen. Für Hintergründe zur Infrastruktur hilft mein Beitrag zu Cronjobs im Shared Hosting, der die Grenzen von shared Umgebungen deutlich macht.
Typische Symptome: so erkenne ich Cron‑Schieflagen
Ein zähes Backend am Vormittag und ruhiger Betrieb in der Nacht deutet häufig auf falsch gelegte oder zu häufige Tasks. Verzögerte Veröffentlichungen, unregelmäßige Backups oder späte E‑Mails zeigen, dass Trigger fehlen oder Caches den Aufruf verhindern. Taucht `wp-cron.php` in Monitoring‑Top‑Listen auf, sammelt sich Overhead an, der den ersten Byte‑Zeitpunkt verschiebt. Häufen sich Deadlocks oder Lock‑Waits, blockieren konkurrierende Aufgaben Datenbankressourcen, was Frontend‑Requests spürbar ausbremst. In Kombination weisen diese Muster klar in Richtung einer Cron‑Architektur, die produktiven Traffic stört.
Der bessere Weg: echte Server‑Cronjobs aktivieren
Ich deaktiviere WP‑Cron konsequent auf Live‑Systemen und lasse einen System‑Cron die Ausführung übernehmen. In der Datei wp‑config.php setze ich dafür die Zeile „define(‚DISABLE_WP_CRON‘, true);“ und entkopple so Cron‑Trigger vom Frontend. Anschließend plane ich im Server‑Crontab einen Aufruf alle 5 bis 15 Minuten, z. B. „*/5 * * * * curl -s https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1“. Damit laufen Jobs zeitgenau, unabhängig von Caches, Proxies und Besucherflüssen. Diese Umstellung reduziert TTFB‑Ausreißer und macht die Ausführung verlässlich steuerbar.
Schritt‑für‑Schritt: sauberes Setup und sinnvolle Intervalle
Ich beginne mit der Deaktivierung des WP‑Cron‑Triggers, lege dann den System‑Cron mit einem moderaten Intervall an und beobachte die Laufzeiten der wichtigsten Aufgaben. Backups und Importe verschiebe ich in ruhige Zeitfenster, damit sie das Tagesgeschäft nicht beeinträchtigen. Ressourcenschwere Jobs bündele ich, sodass nicht zu viele gleichzeitig laufen und Worker blockieren. Danach überprüfe ich Datenbankabfragen auf Indizes und unnötige Scans, um die Laufzeit zu senken. Falls die Umgebung geteilt ist, prüfe ich Limits und erwäge einen Wechsel, bevor Cron‑Spitzen die Nachbarn mitreißen.
Wenn Umstieg noch nicht klappt: Optimierungen und Alternativen
Reduziere übertrieben kurze Intervalle und hinterfrage, ob minütliche Jobs wirklich nötig sind oder ob 5 bis 15 Minuten reichen. Verschiebe E‑Mail‑Wellen, Exporte und Berichte in Zeiten mit weniger Besuchern, damit Frontend‑Requests frei atmen. Identifiziere Plugins mit hohen Cron‑Kosten und ersetze sie, falls sie dauerhaft für Overhead sorgen, statt nur temporär. Prüfe asynchrone Verarbeitung über Worker‑Queues; der Ansatz entkoppelt aufwändige Aufgaben vom Request‑Zyklus und steigert die Zuverlässigkeit. Ein Startpunkt für solche Konzepte ist mein Beitrag zu Worker‑Queues, der die Grundmechanik skizziert.
Rolle des Hostings: worauf ich achte
Ein gutes Hosting liefert ausreichend PHP‑Worker, verlässliche Cron‑Integration und eine sinnvolle MySQL‑Konfiguration. Ich prüfe zudem, ob ein Object‑Cache vorhanden ist und wie Page‑Cache und Proxy‑Layer zusammenspielen, damit Cron‑Trigger nicht ausgebremst werden. Logs und Metriken müssen schnell zugänglich sein, sonst dauert die Ursachenanalyse unnötig lang. Separate Worker‑Prozesse oder Warteschlangen erleichtern die parallele Abarbeitung, ohne die Frontend‑Antwortzeit zu treffen. Wer diese Punkte beachtet, hält Hintergrundjobs verlässlich in Schach und schützt die Performance der Seite.
Wie WP‑Cron intern sperrt – und warum Doppelstarts passieren
Unter der Haube nutzt WordPress einen Transient‑Lock namens `doing_cron`, um gleichzeitige Ausführungen zu vermeiden. Der Lock löst nach einem Timeout wieder, standardmäßig nach einer Minute. Läuft ein Job deutlich länger oder wird der Lock zu früh freigegeben, sind Doppelstarts möglich. Genau das erklärt sporadische Duplikate bei aufwendigen Importen oder E‑Mail‑Wellen. Mit „define(‚WP_CRON_LOCK_TIMEOUT‘, 120);“ kann ich das Zeitfenster anpassen und so lange Tasks besser schützen. Zu hoch darf der Wert aber nicht werden, sonst warten legitime Folgeläufe unnötig lange.
Zusätzlich stößt WP‑Cron sich per Loopback‑Request an `wp-cron.php` selbst an. Filter, Firewalls oder Basic‑Auth blockieren diesen internen HTTP‑Aufruf gerne – die Folge: fällige Ereignisse sammeln sich. Der alternative Modus via „define(‚ALTERNATE_WP_CRON‘, true);“ umgeht manche Blockaden, erzeugt aber zusätzliche Redirects und ist nur ein Notbehelf. Für reproduzierbare Ergebnisse verlasse ich mich nicht auf Loopbacks, sondern auf einen externen System‑Cron, der gezielt triggert.
- Locking anpassen: „WP_CRON_LOCK_TIMEOUT“ auf realistische Laufzeiten abstimmen.
- Loopback‑Fehler vermeiden: Auth‑Ausnahmen oder System‑Cron nutzen.
- Jobs idempotent gestalten: Wiederholter Start darf kein doppeltes Ergebnis erzeugen.
Mehrserver‑Setups und Multisite: wer darf triggern?
In Clustern mit mehreren Web‑Knoten feuern bei Traffic alle Instanzen potenziell WP‑Cron an. Ohne zentrale Steuerung resultiert das in erhöhtem Overhead und Rennbedingungen. Ich definiere daher genau einen Runner: Entweder ein separater Utility‑Knoten oder ein dedizierter Container, der per System‑Cron `wp-cron.php` bzw. WP‑CLI ausführt. Alle übrigen Nodes blocke ich bewusst für Cron‑Trigger.
In Multisite‑Installationen addiert sich die Komplexität: Jeder Blog hat eigene Events. Ich plane daher pro Site klare Läufe oder iteriere gezielt über definierte URLs. Mit WP‑CLI kann ich fällige Ereignisse deterministisch starten und gleichzeitig loggen.
*/5 * * * * wp cron event run --due-now --quiet --url=https://example.com
Bei vielen Sites lohnt sich ein Skript, das die Liste der Subsites ausliest und nacheinander ausführt, um die Datenbank nicht zu überfahren. Wichtig bleibt: ein Runner, klare Reihenfolge, nachvollziehbares Logging.
Sicherheit und Stabilität: Rate‑Limits, Timeouts, Memory
Der Cron‑Trigger selbst sollte robust sein und weder hängen noch zu viel Output produzieren. Ich setze Timeouts und begrenze die Ausgaben, um Crontabs sauber zu halten. Auf Systemen mit restriktiven Firewalls vermeide ich den HTTP‑Weg und rufe PHP direkt auf.
*/5 * * * * /usr/bin/php -d memory_limit=512M -d max_execution_time=300 /path/to/wordpress/wp-cron.php >/dev/null 2>&1
Wenn ich dennoch per HTTP triggere, definiere ich kurze, aber realistische Grenzen und schreibe Fehler in eine Datei, um Ausreißer nachverfolgen zu können.
*/5 * * * * curl -fsS --max-time 30 https://example.com/wp-cron.php?doing_wp_cron >> /var/log/wp-cron.log 2>&1
Wo möglich, schütze ich `wp-cron.php` vor externem Missbrauch, etwa durch IP‑Allowlists oder Regeln, die nur interne Cron‑Runner zulassen. Bei Wartungsfenstern erhöhe ich temporär das `max_execution_time` und das Memory‑Limit für CLI‑Läufe, damit lange Migrationsjobs kontrolliert durchlaufen.
Diagnose mit WP‑CLI und Logging
Für die Analyse nutze ich konsequent WP‑CLI. Ich lasse mir fällige Events und ihre Frequenz anzeigen, identifiziere Ausreißer und starte gezielt Läufe nach.
wp cron event list --fields=hook,next_run,recurrence
wp cron schedule list
wp cron event run --due-now --quiet
Die Größe und Fragmentierung der Cron‑Struktur prüfe ich über die Optionentabelle. Wächst der Eintrag ungewöhnlich, deuten zahllose Einzel‑Events auf eine fehlerhafte Planung.
wp option get cron | wc -c
Ich vermerke in Logs Startzeit, Dauer und Erfolg pro Hook. Damit erkenne ich Muster, setze Budgets (z. B. maximal 30 Sekunden pro Intervall) und verschiebe Ausreißer in ruhige Zeitfenster.
Migrations‑Checkliste: sauber vom WP‑Cron zu System‑Cron
- Inventur: Welche Hooks laufen, wie oft, wie lange? Abhängigkeiten notieren.
- Freeze‑Fenster: Während der Umstellung keine großen Importe/Exporte anstoßen.
- Deaktivieren: „define(‚DISABLE_WP_CRON‘, true);“ setzen und deployen.
- Neuer Trigger: System‑Cron mit 5–15 Minuten Intervall aktivieren.
- Monitoring: Laufzeiten und Fehler der ersten Tage eng beobachten.
- Duplikate: Sicherstellen, dass nicht beide Wege (WP‑Cron und Server‑Cron) parallel feuern.
- Intervalle: Zu feine Frequenzen entschärfen, Batch‑Fenster definieren.
- Rollback: Klarer Rückweg, falls sich neue Engpässe zeigen.
Nach der Migration teste ich gezielt: zeitgesteuerte Veröffentlichung, E‑Mail‑Versand, Backups. Erst wenn diese Kernpfade stabil und pünktlich laufen, ziehe ich die Grenzen enger (kürzere Intervalle) oder erhöhe die Parallelität, wo sinnvoll.
Idempotenz und Wiederaufnahme langer Aufgaben
Weil Cron‑Jobs wiederholt oder verspätet starten können, plane ich sie idempotent. Jede Ausführung prüft den letzten verarbeiteten Stand, arbeitet in kleinen Batches und schreibt Checkpoints. Ein Job, der auf halber Strecke abbricht, darf beim nächsten Lauf einfach weitermachen, ohne doppelte Effekte zu produzieren.
- Chunking: Große Datenmengen in kleine Portionen (z. B. 500 Datensätze) aufteilen.
- Checkpoints: Fortschritt in einer eigenen Option/Tabelle sichern.
- Locks: Pro Hook eine eindeutige Sperre, um Überschneidungen zu verhindern.
- Retry‑Logik: Fehlgeschlagene Batches später erneut versuchen, mit Backoff.
- Einzel‑Events: Für einmalige Aufgaben `wp_schedule_single_event` nutzen, statt künstlich wiederkehrender Hooks.
Solche Muster senken die Fehlerkosten drastisch, weil jeder Lauf autonom stabil bleibt – auch wenn Cron verspätet oder mehrfach triggert.
Staging, Deployments und zeitgesteuerte Veröffentlichungen
Auf Staging‑Systemen deaktiviere ich Cron grundsätzlich, damit keine Massenmails oder Exporte versehentlich rausgehen. Vor Deployments pausiere ich auf Live kurzzeitig lange Tasks, spiele Änderungen ein und starte danach fällige Events bewusst nach („wp cron event run –due-now“). So gerät nichts zwischen die Räder.
Wichtig ist die Zeitzone: WordPress verwaltet die Site‑Zeit separat, der Server‑Cron arbeitet meist in UTC. Pünktliche Veröffentlichungen gelingen konsistent, wenn ich die Divergenz kenne und plane. Leichte Clock‑Skews auf VMs oder Containern berücksichtige ich, indem die Serverzeit synchronisiert und Laufpläne auf „Toleranz“ ausgelegt sind (z. B. alle 5 statt alle 1 Minute).
Nach größeren Plugin‑ oder Schema‑Updates triggere ich kritische Jobs händisch und beobachte die Metriken: CPU‑Last, Query‑Zeit, Fehlerquoten. Treten Ausreißer auf, verteile ich schwere Tasks in die Nacht, entzerre Intervalle und erhöhe Zwischenpausen, bis die Lastkurve wieder glatt ist.
Kurz zusammengefasst: sichere Jobs, schnelle Seite
Auf produktiven WordPress‑Seiten kostet WP‑Cron spürbare Leistung und liefert unzuverlässige Ausführung, weil der Trigger am Traffic hängt. Echte Server‑Cronjobs lösen dieses Kernproblem, machen Zeitpläne verlässlich und entkoppeln Hintergrundarbeit vom Frontend. Mit angepassten Intervallen, optimierten Abfragen und klaren Zeitfenstern verschwinden TTFB‑Ausreißer und Lastspitzen weitgehend. Wer zusätzlich asynchron verarbeitet und Logs im Blick behält, entdeckt Engpässe früh und vermeidet teure Ausfälle. So laufen geplante Aufgaben verlässlich und die Seite bleibt auch unter Last reaktionsschnell.


