WP-Cron is the scheduler inside WordPress. Scheduled posts, plugin update checks, newsletter queues, automatic cleanups: they all sit in a list of tasks, and WP-Cron runs whatever is due. The catch is in how it decides “now”. WP-Cron does not run on a clock. It runs when somebody loads a page. No visitor, no cron.

That single design choice explains most “my scheduled post never went out” and “the plugin was supposed to do that at midnight” tickets we get. Here is how to run it by hand when you need to, and how to make it reliable so you never need to again.

Run WP-Cron manually, right now

The simplest way is to open this URL in your browser. It boots WordPress, runs every task that is due, and returns an empty page:

https://yoursite.com/wp-cron.php?doing_wp_cron

From a terminal, same thing:

curl -s https://yoursite.com/wp-cron.php?doing_wp_cron

If you have WP-CLI on the server, you get something better: you can see the queue before you run it.

wp cron event list          # what is scheduled, and when
wp cron event run --due-now # run everything that is overdue
wp cron test                # checks that WP-Cron can be spawned at all

No terminal? The free WP Crontrol plugin shows the same list under Tools → Cron Events, with a “Run now” link next to each task. It is the fastest way to find out whether a task is stuck or simply never scheduled.

Why scheduled tasks quietly stop

Four causes, in the order we see them:

  • Page caching. This is the big one and almost nobody suspects it. A cache plugin or your host serves visitors a saved HTML copy, so WordPress never boots for them, so WP-Cron never gets its chance. A busy cached site can miss its schedule for hours.
  • Low traffic. A site with ten visits a day runs its cron ten times a day, at random moments.
  • Your host disabled it. Many managed hosts set DISABLE_WP_CRON and replace it with a real cron on their side. That is good, as long as their cron is actually turned on for your site. Check the hosting panel.
  • A stuck lock. WP-Cron sets a lock while it runs. If a task crashed mid-way, the lock can linger for a minute and every request during that minute skips the queue. wp cron event run --due-now clears it.

Make it reliable: a real cron job

The fix is to stop tying the scheduler to visitors. First, tell WordPress not to fire WP-Cron on page loads. In wp-config.php:

define( 'DISABLE_WP_CRON', true );

Then have the server call it on a clock. On any Linux host with cron access, crontab -e and add one line, every five minutes:

*/5 * * * * curl -s https://yoursite.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1

Or, if WP-CLI is installed, the same schedule without going through the web server at all:

*/5 * * * * cd /var/www/yoursite && wp cron event run --due-now > /dev/null 2>&1

Five minutes is a good default. Every minute is fine for a site that sends time-sensitive emails; every fifteen is fine for a blog. Visitors are no longer slowed down by a task that happens to be due when they land, and the queue runs whether anyone is around or not.

Before you set any of this up, look in your hosting panel. Kinsta, WP Engine, SiteGround, Cloudways and most managed hosts already run a server-side cron for you, or have a switch for it. Turning that on is the whole job.

No server access? Use an external pinger

On shared hosting without cron, an outside service can hit the URL for you. EasyCron and cron-job.org both have free tiers that cover one URL every few minutes. Point them at https://yoursite.com/wp-cron.php?doing_wp_cron, keep DISABLE_WP_CRON on, done. Here is the setup I used for years on a small site that scheduled social posts:

EasyCron dashboard configured to trigger WordPress WP-Cron

What “?doing_wp_cron” means

You will see that parameter appended by WordPress itself and by plugins. It is a signal that the request is a cron spawn, so WordPress runs the queue instead of rendering a page, and it carries a timestamp used as the lock. It is harmless in your logs, and you should keep it on any URL you schedule.

Which of our plugins depend on it

Anything that works in the background does. Database Cleaner runs its automatic cleanups from WP-Cron, Meow Mailer sends its newsletter queue from it, and the scheduled triggers in Meow Workflow fire from it. If one of them “does nothing at the scheduled time”, run wp cron event list first: nine times out of ten the task is there and waiting, and the scheduler is what never woke up 🙂