
WP-Cron does not run on a clock. It runs on page loads, which is why your scheduled post missed its slot and your renewal email showed up three hours late.
Everyone will tell you to replace it with a real server cron. That is often right, and it is not always right, so this piece starts from your symptom instead of from the tool.
Principaux enseignements
- WP-Cron is triggered by page loads, not by the system clock, so a low-traffic site misses schedules and a high-traffic site does the same check thousands of times an hour.
- The standard fix is
define( 'DISABLE_WP_CRON', true );enwp-config.phpplus a real server cron callingwp-cron.phpon a fixed interval. - Set the constant and forget the server cron and every scheduled task on your site silently stops. The constant kills the trigger, it does not replace it.
- Action Scheduler is a queue, not a clock. Its own FAQ says it is initiated by WP-Cron by default and that past-due actions are normal, so it fixes throughput, not punctuality.
- External cron services need no server access and add monitoring, at the cost of a single point of failure outside your site.
- Default WP-Cron is genuinely fine for most small blogs. WordPress documents its queue-and-retry behavior as a feature, and it is one.
Why WP-Cron misses its schedule
WP-Cron only fires when somebody loads a page. WordPress checks the list of due tasks on each page load and runs whatever is overdue, so if nobody visits your site between 2:00 PM and 5:00 PM, the task you scheduled for 2:00 PM runs at 5:00 PM.
That is not my interpretation, it is what WordPress documents. The Plugin Handbook’s cron chapter puts it plainly: “WP-Cron does not run constantly as the system cron does; it is only triggered on page load”, and it gives almost exactly that 2:00 PM example.
So the name is the problem. It is called cron, it behaves like a to-do list that gets checked whenever someone walks past it.
The failure runs in both directions, which is the part most guides skip. A quiet site misses its schedules, and a busy site runs the same check on every single request, which WP Engine’s documentation says “causes the server to work harder than it really needs to and can cause slowness, timeouts, and other performance issues.”
Start from your symptom, not from the tool
Different symptoms need different fixes, and picking the tool first is how people end up with an external cron service pinging a site whose real problem was a bloated actions table. Find your symptom in this table, then read the section it points to.
| What you are seeing | What is actually wrong | What fixes it |
|---|---|---|
| A scheduled post shows “Missed schedule” | Nobody loaded a page at the moment the post was due | A real server cron, so the trigger stops depending on visitors |
| WooCommerce emails and renewals arrive late | Same trigger problem, on a queue that matters more | Server cron, and check Action Scheduler for stuck jobs |
Thousands of rows in wp_actionscheduler_actions | A throughput problem, not a timing problem | Action Scheduler tuning and cleanup, not a new trigger |
| Your site gets very little traffic | The trigger fires too rarely to trust | Server cron or an external cron service |
| High traffic and a slow TTFB under load | The cron check is running on every request | DISABLE_WP_CRON plus a server cron on a fixed interval |
| Nothing is actually broken | Nothing | Leave it alone. Really |
Notice how few of those rows say “replace WP-Cron”. That is deliberate, and it is where I part company with the advice you will find at the top of the search results.

Server cron, Action Scheduler and external services, compared
These three are not competitors, and treating them as a shortlist is the first mistake. A server cron replaces the trigger, Action Scheduler manages a queue of jobs, and an external service replaces the trigger from outside your server.
| Real server cron | Action Scheduler | External cron service | |
|---|---|---|---|
| What it actually does | Calls wp-cron.php on the system clock | Queues, batches, retries and logs background jobs | Calls wp-cron.php from another server |
| Fixes punctuality? | Oui | No. It inherits WP-Cron’s trigger by default | Oui |
| Fixes throughput? | Non | Yes, this is its whole job | Non |
| Needs server access? | Yes, crontab or a host panel | No, it ships inside WooCommerce and others | Non |
| Main risk | You set the constant and forget the cron | Mistaking it for a scheduling fix | A single point of failure you do not control |
| Idéal pour | Fixed publish times, renewals, low-traffic sites | Stores with thousands of queued jobs | Managed hosting with no cron access |
To inspect what is actually scheduled on your site, WP Crontrol is the standard plugin, and WP-CLI gives you the same list with wp cron event list. Look before you change anything.
How to switch WP-Cron to a real server cron
It is two steps: turn off the page-load trigger, then have the system clock call the same file on a schedule. Do the second one, or your site quietly stops running every scheduled task it has.
Step 1. Add this to wp-config.php, above the line that says “That’s all, stop editing”:
define( 'DISABLE_WP_CRON', true );Step 2. Add a real cron job on the server, either through your host’s control panel or in crontab. Every five minutes is a sensible default:
*/5 * * * * wget -q -O - https://yoursite.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1Now the important part, and it is the failure mode nobody warns you about. DISABLE_WP_CRON does not disable scheduling, it disables the trigger.
Your events are still sitting in the database waiting their turn. Nothing is coming to run them. So the posts do not publish, the emails do not send, the backups do not fire, and WordPress reports no error at all, because from its point of view the queue is simply not being asked.
One more caveat if you are on managed hosting. WP Engine’s own documentation says it does not technically support true server crons and instead offers an Alternate Cron service that calls wp-cron.php for you, which requires the same constant. So “just use crontab” is not even literally possible on some hosts, and the guides that say it never mention that.
Action Scheduler is a queue, not a clock
Action Scheduler will not make your tasks punctual, because it does not own the trigger. Its own FAQ says it “is initiated by WP-Cron” by default and is “designed to work alongside WP-Cron and not change any of its behaviour”, and it states that past-due actions are normal.
Read that again, because it is routinely sold as the fix for late jobs. It inherits the exact trigger problem it is supposed to solve.
I can show you this on our own site rather than argue it. WordPress Site Health here reports a scheduled event running late, and the event it names is action_scheduler_run_queue, which is the WP-Cron event whose job is to run the Action Scheduler queue.

So what is Action Scheduler good for? Volume. It batches, retries, logs, and gives you an admin screen, and it stores everything in its own actionscheduler_ tables rather than dumping it all into wp_options.
Here is what that looks like on a real store. Our site has logged 1,697 actions, 1,565 completed and 123 failed, and I would not have known the failures existed without opening this screen.

That is the honest summary of the tool. It is excellent at managing thousands of jobs, and it is the wrong thing to reach for when your complaint is “this needed to happen at 3:10 AM.”
What external cron services buy you, and what they cost you
An external cron service calls your wp-cron.php from somewhere else on the internet, on a fixed schedule. You need no server access at all, and you usually get monitoring and alerting, which a crontab line will never give you.
The trade is that your site’s heartbeat now lives outside your site. If the service goes down, or your account lapses, or the route to your server breaks, the trigger is gone and nothing on your server notices, because nothing on your server was ever watching.
That is my reasoning, not a documented flaw anyone publishes, and I would still take the trade on a managed host with no cron access. The monitoring is worth something, and a missing trigger you get alerted about beats a missing trigger you discover a week later.
Who genuinely does not need to replace WP-Cron
If your site has no fixed-time obligations, WP-Cron is fine and you can stop reading. A blog that publishes when you press publish, with no store, no renewals, and no time-sensitive emails, loses nothing when a cleanup task runs at 5:00 PM instead of 2:00 PM.
WordPress makes this case itself, and it is the quote the “always replace it” crowd never shows you. From the same handbook chapter:
With the system scheduler, if the time passes and the task did not run, it will not be re-attempted. With WP-Cron, all scheduled tasks are put into a queue and will run at the next opportunity (meaning the next page load). So while you can’t be 100% sure quand your task will run, you can be 100% sure that it will run eventually.
That is a real design tradeoff, honestly stated. A system cron that fires while your site is erroring simply misses that run and moves on, while WP-Cron holds the job in the queue and retries it at the next opportunity.
Neither model is better in the abstract, they fail differently. You are choosing between “it will run, I am not sure exactly when” and “it will run at 3:10 AM, or it will not run at all.”
It is worth noticing who benefits from the blanket advice, too. Hosts and cron-monitoring vendors are the ones publishing most of it, and the mechanics they give you are correct. The “always” is what I would push back on.
What this site actually runs, and why
We fall firmly in the “timing matters” camp, so we run the server-side trigger. This blog publishes on fixed slots, and two posts went live this week at their exact scheduled minutes, one at 3:10 AM and one at 3:30 PM Dhaka time, while my machine was off and I was asleep.
That only works because the trigger does not depend on a visitor happening to arrive at 3:10 AM. Almost nobody is browsing an SEO blog at that hour, which is exactly the low-traffic failure case from the table above.
Our host is Hostinger, and like most hosts it exposes a Cron Jobs screen in the panel, so the setup is the two steps above and nothing more clever than that.
The war story that got us here is worth reading if you sell anything: I wrote up the exact case where WooCommerce emails and renewals were arriving late and setting up a real cron job fixed it. This article is the general version of that fix.
One distinction worth keeping straight while you are in there: late email is a scheduling problem, and email that never arrives at all is usually a delivery problem, which is a different fix entirely. If you are not sure which one you have, check whether your mail is even leaving the building by setting up SMTP properly first.
And if your reason for looking at this is server load rather than timing, the page-load overhead is real but small, and it is rarely the thing actually slowing you down. I have written about what genuinely moves the needle when a WordPress site has to handle serious traffic, and about INP and responsiveness for the front-end side of it.
So should you replace WP-Cron?
If anything on your site has to happen at a specific time, yes, and do it today. Scheduled posts, subscription renewals, transactional emails, and nightly backups all deserve a trigger that does not depend on a stranger loading your homepage.
If nothing on your site has a deadline, leave it alone. You would be adding a moving part and a new way to fail, in exchange for a punctuality you were never using.
And if you take one thing from this: when you set that constant, go and confirm the server cron is actually running before you close the terminal. A site with DISABLE_WP_CRON and no cron job looks perfectly healthy right up until the day you notice nothing has published in a week.
Scheduled tasks still firing late?
If your posts, emails, or renewals are still missing their times after this, nous contacter ou m'envoyer un courriel and I will help you work out which of the three problems you actually have.
Want our posts to show up more often on Google?
One step & Google will surface this site in your Top Stories.
