Action Scheduler past-due actions behind Cloudflare: when the firewall blocks WP-Cron

WooCommerce says "Action Scheduler: past-due actions found; something may be wrong", the number keeps climbing, and nothing in any log explains it. If the site sits behind Cloudflare (or any web application firewall), the firewall is one of the most likely causes - and one of the hardest to see.

Why a firewall stops the queue without an error

WP-Cron is not a timer. When someone loads a page, WordPress sends a second request from your server back to your own site, to /wp-cron.php, and that request is what runs Action Scheduler's queue.

That request comes from your own server's IP address, carries no browser, and goes straight to a PHP file. To a firewall it looks like a bot. Bot Fight Mode, "I'm Under Attack" mode and many rate-limiting rules block it. The visitor's page still loads normally, so the site looks healthy - but the queue never runs, and WordPress never receives the request, so there is nothing to log.

Confirm it in one place

Open Tools → Site Health. If the loopback request fails, it is listed there as a failed loopback. When loopback fails, nothing you change inside WooCommerce will make the queue run.

If you can, also look at Cloudflare → Security → Events, filtered to the path /wp-cron.php. Blocked or challenged requests from your own server's IP confirm it.

Fix 1: let the queue through the firewall

Action Scheduler starts its queue through two addresses, not one: /wp-cron.php and /wp-admin/admin-ajax.php. Both have to get through.

  1. Bot Fight Mode: it does not run through custom rules, so a skip rule cannot exempt anything from it. On Cloudflare's free plan, switch it off under Security → Bots.
  2. WAF and rate limiting: in Security → WAF → Custom rules, create a rule with the expression below, set the action to Skip, and choose the managed rules and rate limiting rules.
(http.request.uri.path in {"/wp-cron.php" "/wp-admin/admin-ajax.php"})

Within a few minutes the past-due count should start to fall. If it does not, use Fix 2, which avoids the firewall entirely.

Fix 2 (better): stop using a web request at all

If you have shell or cron access at your host, run the queue with WP-CLI instead. There is no HTTP request, so there is nothing for a proxy, firewall or rate limit to judge. In wp-config.php:

define( 'DISABLE_WP_CRON', true );

Then add a server cron job that runs every five minutes:

cd /path/to/wordpress && wp cron event run --due-now

Only set DISABLE_WP_CRON once the server cron is in place; with neither, nothing runs at all.

Not sure the firewall is the cause?

StallRadar Past-Due Monitor is a free plugin on WordPress.org that shows which plugin's actions are stuck, checks whether the queue can run (WP-Cron settings, loopback, time limits), and says what to fix first - instead of just deleting rows.

Get it free from WordPress.org

Not affiliated with Cloudflare, WooCommerce or Automattic.

Questions

Why is there no error anywhere?

The blocked request is the one your server sends to itself. The visitor's page loads normally, so nothing looks broken, and WordPress never receives the request it would have logged.

Should I press Run on each past-due action?

No. Run is a debugging tool for one stuck job. Once the queue can run again, it works through the backlog by itself.

Is it safe to skip the firewall for wp-cron.php?

The skip covers only the two addresses WordPress uses to start its scheduled tasks, and only for the rules you choose. If you would rather not open anything, use a server cron job with WP-CLI instead, which needs no web request.

Related fixes