Missat schema i WordPress: därför kör WP-Cron inte dina inlägg, backuper och mejl (och den riktiga fixen)
Ett inlägg som aldrig publicerades, en backup som aldrig kördes
Du har schemalagt ett blogginlägg till klockan 08.00 en tisdagsmorgon. Klockan blir nio, tio, halv tolv, och inlägget ligger fortfarande som utkast. Du loggar in och ser texten "Missat schema" i stället för "Publicerat". Eller så upptäcker du en dag att backup-pluginet inte har sparat något på tre veckor, fast allt såg konfigurerat rätt ut. Eller så ringer en kund och undrar var bekräftelsemejlet från beställningen tog vägen, trots att ordern går igenom i WooCommerce.
Söker du på "schemalagt inlägg publicerades inte", "wordpress backup körs inte" eller "wp-cron fungerar inte" hittar du samma bakomliggande orsak i alla tre fallen. Det korta svaret är att WordPress schemaläggare, WP-Cron, inte är en klocka som tickar i bakgrunden. Den är en dörrklocka som bara ringer när någon faktiskt går förbi och trycker på den, det vill säga när en besökare laddar en sida på din sajt. Ingen trafik, inget klockslag, inget jobb körs.
Att det ändå fungerar på de flesta sajter är webbhotellets förtjänst, inte WordPress egen konstruktion. Webbhotellet bakom sajten har satt upp en riktig cron som knackar på dörren åt dig, med jämna mellanrum, oavsett besökare. Det är den distinktionen den här artikeln handlar om, och den avgör om dina schemalagda inlägg, backuper och mejl faktiskt går ut eller bara ser ut att vara inställda.
Vad "missat schema" egentligen betyder
WordPress egen utvecklardokumentation beskriver mekaniken rakt av. Enligt WP-Cron-handboken fungerar systemet genom att "kontrollera, vid varje sidladdning, en lista över schemalagda uppgifter för att se vad som behöver köras" ("WP-Cron works by checking, on every page load, a list of scheduled tasks to see what needs to be run"). Det är sidladdningen, inte klockan i sig, som utlöser kontrollen.
Dokumentationen är lika tydlig om vad det innebär i praktiken. "WP-Cron körs inte kontinuerligt som systemets cron gör, det utlöses bara vid sidladdning" ("WP-Cron does not run constantly as the system cron does; it is only triggered on page load"). Har din sajt mycket trafik dygnet runt märker du sällan av detta, för det kommer alltid en besökare inom några sekunder efter det schemalagda klockslaget. Men på en lågtrafiksajt, ett nystartat projekt, en nischbutik som mest får besök kvällstid, en intern sajt för ett litet företag, ser bilden annorlunda ut.
WordPress ger själva exemplet i sin dokumentation: "Schemaläggningsfel kan uppstå om du schemalägger en uppgift till klockan 14.00 och inga sidladdningar sker förrän klockan 17.00" ("Scheduling errors could occur if you schedule a task for 2:00PM and no page loads occur until 5:00PM"). Det är precis det som händer när ett inlägg får statusen "Missat schema". Uppgiften låg och väntade i kön, men ingen besökare kom förbi och ringde på dörrklockan innan tidsfönstret för att köra den stängdes.
Loopback-anropet: dörren som måste stå öppen
Även när en besökare väl laddar en sida händer inte allt i den besökarens webbläsare. WordPress skickar i stället ett internt HTTP-anrop till sin egen fil wp-cron.php, ett så kallat loopback-anrop (loopback request), där servern i praktiken ringer upp sig själv. Anropet är icke-blockerande och har en mycket kort tidsgräns, och en tillfällig spärr (en så kallad transient med namnet doing_cron) hindrar att flera anrop startar samtidigt. Poängen med konstruktionen är att den vanliga besökaren aldrig ska märka att någonting extra hände i bakgrunden.
Problemet uppstår när servern av någon anledning inte kan nå sig själv över HTTP. Då utlöses visserligen kontrollen vid sidladdningen, men själva jobbet startar aldrig. De vanligaste orsakerna till att loopback-anropet misslyckas är:
- Lösenordsskydd eller basic auth på sajten, till exempel på en staging- eller utvecklingsmiljö som ligger bakom en inloggningsruta.
- Ett säkerhetsplugin eller en brandvägg som tolkar det interna anropet som misstänkt trafik och blockerar det.
- Ett SSL/SNI-matchningsfel, där certifikatet inte gäller korrekt för det interna anropet mot sajtens egen adress.
- En spärr hos webbhotellet självt, där servern av policyskäl inte tillåter att en sajt anropar sin egen domän.
WordPress har ett inbyggt sätt att avslöja exakt detta. Under Verktyg → Hälsa (Site Health) i adminpanelen, fliken Info respektive Status, kontrollerar WordPress om loopback-anropet fungerar. Fungerar det inte visas felmeddelandet "loopback request failed". Det är ett direkt bevis på att mekanismen som ska utlösa schemalagda jobb är trasig, oavsett vad den bakomliggande orsaken är i just ditt fall.
WP-Cron är en dörrklocka, inte en klocka, och att den ringer pålitligt är en egenskap hos ditt webbhotell, inte hos WordPress.
Så ser det ut hos svenska webbhotell
Här skiljer sig verkligheten åt beroende på vilken typ av kontrollpanel du har. Kör du på ett webbhotell med cPanel eller DirectAdmin, vanligt hos exempelvis Oderland, Miss Hosting och Inleed, får du tillgång till ett verktyg som heter Cron Jobs där du fritt kan lägga upp egna schemalagda kommandon. Verktyget finns där. Kopplingen mellan det verktyget och WordPress specifika behov är det däremot upp till dig att skapa själv, panelen gör det inte automatiskt.
| Webbhotell / panel | Cron-verktyg | WP-Cron-koppling |
|---|---|---|
| cPanel/DirectAdmin (t.ex. Oderland, Miss Hosting, Inleed) | Cron Jobs, fritt konfigurerbart schema | Ej förkopplad, du sätter upp den själv |
| Oderland specifikt | Cron Jobs i cPanel | Namngiven supportartikel med steg-för-steg-guide, enligt Oderlands egen supportartikel |
| Loopia | URL-schemaläggare i Kundzonen | Kan i teorin peka mot wp-cron.php, men dokumenteras inte för WordPress |
| one.com | Ingen publik cron-dokumentation hittad | Okänt, fråga hosten |
Oderland är ett förtjänat positivt exempel här. Enligt bolagets egen supportartikel finns en namngiven steg-för-steg-guide som instruerar kunden att sätta DISABLE_WP_CRON i wp-config.php och lägga upp ett eget cron-jobb som körs var femte till femtonde minut. Det är precis det mönster som WordPress egen dokumentation förespråkar, förklarat konkret för just den plattformen.
Loopia och one.com har mer låsta paneler, och det förändrar bilden på ett annat sätt. Loopia erbjuder en generell URL-schemaläggare i Kundzonen som i teorin kan pekas mot en wp-cron.php-fil, men vi har inte hittat någon dokumentation från Loopia som beskriver den kopplingen specifikt för WordPress. One.com saknar helt publik dokumentation om cron-hantering för WordPress-sajter i sina paneler. Frånvaro av dokumentation är inte samma sak som frånvaro av funktion, det betyder bara att du som kund måste fråga supporten direkt eller undersöka panelen själv innan du drar slutsatser.
Samma försiktighet gäller loopback-frågan rakt igenom. Vi har inte kunnat belägga generellt vilka svenska webbhotell som tillåter respektive blockerar loopback-anrop, och det varierar sannolikt mellan olika paket och serverkonfigurationer hos samma leverantör. Den rätta frågan att ställa sin nuvarande eller blivande host är enkel att formulera. Kör ni en riktig cron för WordPress, och tillåter ni loopback-anrop mot sajtens egen domän? Ett tydligt ja på båda är vad du vill höra.
Allt som är schemalagt drabbas
Det som gör den här buggen lurig är att den aldrig visar sig som ett enda fel. Den visar sig som flera till synes orelaterade problem, som alla har samma rot i att WP-Cron inte utlöses eller inte kan nå sig själv. Ett schemalagt inlägg som fastnar i "Missat schema" är bara det mest synliga exemplet.
Backuper hör till samma familj av symptom. Många backup-plugin förlitar sig helt på WP-Cron för att köra sina schemalagda säkerhetskopior, och en trasig cron ger dig ingen krasch, bara tystnad. Loggen visar helt enkelt inget nytt. Vi går igenom hur du bygger en backup-rutin som faktiskt håller i vår genomgång av WordPress-backup som fungerar, och mer generellt om backup-strategi oavsett plattform i artikeln om backup-strategi för en webbplats.
WooCommerce-butiker har en extra känslig beroendekedja, eftersom orderbekräftelser, lagerpåminnelser och delar av betalningsflödet utlöses via samma schemaläggningssystem. Kombinerar man det med begränsade resurser på ett billigt delat webbhotell blir felbilden ofta rörig, med krascher som ser ut att komma från själva butiksplattformen men som i grunden handlar om serverns hantering av schemalagda och parallella processer. Vi har utvecklat det separat i artikeln om varför WooCommerce kraschar på billigt webbhotell.
Utebliven e-post, exempelvis bekräftelsemejl som aldrig når kunden, kan i vissa fall ha samma cron-koppling men lika ofta handla om helt andra saker som SPF, DKIM och avsändardomän. Vi reder ut skillnaden i artikeln om WordPress-mejl som hamnar i spam. Och om du funderat på att stänga av WP-Cron helt för att vinna prestanda utan att sätta upp en ersättning, är det värt att läsa vår genomgång av att optimera WordPress laddtid innan du gör det, eftersom just den genvägen är den vanligaste vägen in i det tysta haveriet vi beskriver här.
Ett annat symptom som sällan kopplas till WP-Cron är resursbegränsningar som inte syns på prissidan, till exempel gränser för antal filer (inoder) eller processer (entry processes). Även om orsaken där oftast ligger på en annan nivå än schemaläggningen, delar den samma grundegenskap. Sajten ser ut att fungera, men något i bakgrunden gör inte det den ska. Vi går igenom det i artikeln om dolda resursgränser i WordPress.
Den riktiga fixen: stäng av sidladdningscron, sätt en riktig
Produktionsmönstret som WordPress själv rekommenderar är egentligen enkelt i två steg, men det andra steget missas ofta helt. Första steget är att slå av den sidladdningsdrivna cronen i wp-config.php:
define('DISABLE_WP_CRON', true);
Det andra steget, och det som lätt glöms bort, är att ersätta den med en riktig systemcron som körs på servern oavsett trafik. WordPress egen dokumentation om att koppla WP-Cron till systemets schemaläggare ger exemplet:
0 0 * * * wget --delete-after http://YOUR_SITE_URL/wp-cron.php
Den officiella dokumentationen är försiktig i sin formulering. Där står bara att sidladdningsdriven cron "inte längre behövs och bidrar till extra resursanvändning" ("is no longer necessary and will contribute to extra resource usage") när en riktig cron finns på plats. Vad dokumentationen inte säger lika tydligt är att steg två är obligatoriskt om du redan har genomfört steg ett. Stänger du bara av DISABLE_WP_CRON utan att lägga upp ersättningscronen slutar allting att köras, tyst och utan felmeddelande. Inga inlägg publiceras på schema, inga backuper tas, inga WooCommerce-mejl skickas.
Exemplet ovan, 0 0 * * *, kör bara en gång per dygn. Det räcker för att inte råka missa varje enskilt jobb för alltid, men det är alldeles för glest för de flesta sajter, där schemalagda inlägg, prenumerationsmejl och backuper ofta behöver köras betydligt tätare. Ett vanligare och rimligare intervall är var femte minut:
*/5 * * * * wget -q -O - http://dinsajt.se/wp-cron.php >/dev/null 2>&1
Exakt hur du lägger upp raden skiljer sig åt beroende på panel. I cPanel och DirectAdmin gör du det under Cron Jobs, med URL:en till din egen wp-cron.php-fil. Har din host en låst panel utan fritt cron-verktyg, som Loopia eller one.com kan ha beroende på paket, är nästa steg att fråga supporten hur du bäst löser kopplingen hos just dem, eller om deras befintliga URL-schemaläggare (om en sådan finns) kan pekas mot filen.
Reservlösningen ALTERNATE_WP_CRON
Finns det lägen där du varken kan sätta upp en riktig systemcron eller få loopback-anropet att fungera finns en tredje inställning, ALTERNATE_WP_CRON. Den fungerar genom en omdirigeringsbaserad reservmetod, där cron-körningen läggs på en vanlig besökares sidhämtning i stället för via det interna loopback-anropet.
define('ALTERNATE_WP_CRON', true);
Se det här som en nödlösning, inte ett förstahandsval. Metoden kan ge synliga omdirigeringar i webbläsarens adressfält för besökaren, och den är generellt mindre pålitlig än en riktig systemcron. Har du möjlighet att lösa det med en schemalagd cron hos ditt webbhotell, oavsett om det sker via panelens eget verktyg eller genom att fråga supporten, är det alltid att föredra framför ALTERNATE_WP_CRON.
Så verifierar du att det fungerar
Innan du drar slutsatsen att felet är löst finns det en enkel ordning att gå igenom, snarare än att bara vänta och hoppas.
- Kontrollera Webbplatshälsa. Gå till Verktyg → Hälsa i adminpanelen och se om meddelandet "loopback request failed" eller andra schemaläggningsvarningar dyker upp. Det är den snabbaste bekräftelsen på om själva mekanismen är trasig.
- Installera WP Crontrol. Pluginet visar exakt vilka händelser som faktiskt är schemalagda, när de senast kördes och om något hänger fast i väntan. Det gör felsökningen konkret i stället för att gissa.
- Bekräfta fixen med ett riktigt test. Lägg upp ett testinlägg schemalagt några minuter fram i tiden och se att det publiceras på utsatt klockslag, eller kontrollera i Crontrol att kända händelser faktiskt exekveras och inte längre står som väntande.
Är svaret nej på någon av de tre punkterna vet du att den bakomliggande orsaken fortfarande finns kvar, oavsett hur många plugin du har installerat eller hur många gånger du har sparat om schemaläggningsinställningarna. Problemet ligger sällan i själva WordPress-installationen. Det ligger i vägen mellan sajten och servern som ska ringa på dess egen dörrklocka.