WP-Cron Not Working? Fix Missed Scheduled Posts and Stuck WooCommerce Actions
Scheduled posts show "Missed schedule". WooCommerce emails go out hours late. Subscription renewals don't run, backups don't happen, and WooCommerce → Status → Scheduled Actions shows a growing pile of "Past-due" actions. All of these point to one thing: WP-Cron isn't running. Here's why that happens and how to set it up so it runs reliably.
How WP-Cron actually works
WP-Cron isn't a real cron job. WordPress has no background process. Instead, on each page load, WordPress checks whether any scheduled task is due. If one is, it sends a request to yoursite.com/wp-cron.php (called a loopback request) to run it in the background.
That design has two weak points:
- No visitors means no cron. On a low-traffic site, or a site whose pages are all served from cache, WordPress may not run for hours, and nothing scheduled happens.
- The loopback request can fail. If the server can't reach itself over HTTP, cron never fires even on busy sites.
Step 1: Find out what's wrong
Check Site Health
Go to Tools → Site Health. Two results matter here:
- "A scheduled event has failed" or "A scheduled event is late": cron is running badly or not at all.
- "Your site could not complete a loopback request": the server can't call itself, so
wp-cron.phpnever runs.
Check with WP-CLI
# Can WordPress spawn cron?
wp cron test
# What's scheduled, and is anything overdue?
wp cron event list --fields=hook,next_run_relative,recurrence
# Is cron disabled in wp-config.php?
wp config get DISABLE_WP_CRON
Events with a next_run_relative in the past ("2 hours ago") confirm cron isn't firing. If DISABLE_WP_CRON returns true, someone turned WP-Cron off, which is fine only if a real server cron job replaced it. Often, nobody set one up.
Step 2: Fix failed loopback requests
If Site Health reports a failed loopback, the usual causes are:
- HTTP Basic Auth on a staging or password-protected site blocks the server's request to itself.
- A firewall or security plugin blocks requests to
wp-cron.php, or blocks the server's own IP. - Cloudflare or a WAF rule challenges the request. A bot check can't be passed by a server.
- DNS on the server resolves your domain to the wrong IP (common right after a migration), so the loopback goes to the old server.
- A plugin with a PHP error breaks every request, including the loopback. Check
debug.log.
You can often get cron working anyway by switching to a real server cron job, which avoids the loopback entirely. That's the next step, and it's what I set up on every production site.
Step 3: Replace WP-Cron with a real server cron job
First, stop WordPress from triggering cron on page loads. Add this to wp-config.php, above the "stop editing" line:
define( 'DISABLE_WP_CRON', true );
Then add a cron job on the server that runs scheduled tasks every 5 minutes.
Best option: WP-CLI (if your host has it)
*/5 * * * * cd /home/user/public_html && /usr/local/bin/wp cron event run --due-now --quiet >/dev/null 2>&1
This runs PHP directly with no HTTP request, so there's no firewall, auth or timeout problem. Use the real path to your site and to wp (find it with which wp).
Alternative: call wp-cron.php over HTTP
*/5 * * * * curl -s https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1
Simple, and it works on almost any host, but the request has to get past your firewall and Cloudflare like any other request.
Adding the job in cPanel
Go to Advanced → Cron Jobs, pick "Once Per Five Minutes" from Common Settings, and paste the command (without the */5 * * * * part, since cPanel fills in the timing). Managed WordPress hosts like Kinsta and WP Engine often run server cron for you already. Check their docs before adding your own.
Step 4: Clear the Action Scheduler backlog (WooCommerce)
WooCommerce, subscriptions plugins, many email plugins and others queue background work in Action Scheduler, which runs on top of WP-Cron. Once cron is fixed, the backlog works through on its own, a batch at a time. For a large backlog, you can process it faster from the command line:
# See what's pending
wp action-scheduler list --status=pending --format=count
# Process the queue now
wp action-scheduler run --batch-size=100
Also look at failed actions under WooCommerce → Status → Scheduled Actions. If the same hook fails thousands of times, the problem isn't cron. That action itself is broken, often a webhook pointing at a URL that no longer exists. Open one of the failed actions to read its log message.
Fixing posts already marked "Missed schedule"
Posts that missed their publish time won't publish on their own afterwards. Once cron works, publish them manually, or list them with:
wp post list --post_status=future --fields=ID,post_title,post_date
Posts with a past post_date in that list missed their schedule. Open each one and click Publish.
Verify it's working
After an hour, run wp cron event list again. Nothing should be more than a few minutes overdue. The WP Crontrol plugin gives you the same view in wp-admin, plus the ability to run individual events manually, which is very handy for testing.
Written by
Sameer Hussain, Senior Shopify & WordPress Engineer
Top Rated on Upwork and the Shopify & WordPress engineer at Inceptus Digital. Short on time, or not comfortable editing code? Send me your URL and the error on Upwork and I'll tell you how I'd fix it before you commit.
Need Custom Engineering or Growth Systems?
Our team helps ambitious brands build bespoke software, AI integrations, and high-conversion platforms.