Your WordPress site randomly takes 3-4 seconds to load. You check again – 400ms. Refresh – back to 3 seconds. Then it’s fast again for the next 20 requests.
You blame the hosting. You enable caching. You optimize images. Nothing fixes the random spikes. The problem isn’t your server, your theme, or your plugins. It’s wp-cron – and it’s been silently degrading your performance since the day you installed WordPress.
What wp-cron actually does on every page load
WordPress doesn’t have access to your server’s task scheduler. So it built its own pseudo-cron system. Every single time someone loads a page on your site, WordPress runs this check: are there scheduled tasks waiting to execute?
Here’s what happens under the hood. On every page load, WordPress calls spawn_cron(). This function:
- Checks the
doing_crontransient to see if another cron process is already running - Calls
wp_get_ready_cron_jobs()to find tasks with timestamps in the past - If tasks are due, fires a non-blocking HTTP POST request back to your own site at
/wp-cron.php - Sets a transient lock so other page loads don’t trigger duplicate runs
Read that again. WordPress makes an HTTP request to itself on every page load where cron tasks are due. Your server is literally calling itself.
Why this causes random slowdowns
The self-HTTP request is supposed to be non-blocking – WordPress sets a timeout of 0.01 seconds and 'blocking' => false. In theory, the page continues loading while cron runs in the background.
In practice, several things go wrong:
1. The loopback request eats a PHP worker
When WordPress fires that request to wp-cron.php, your server needs to handle it like any other request. That means a PHP worker gets occupied running scheduled tasks – publishing scheduled posts, sending emails, running plugin maintenance, processing WooCommerce actions, clearing expired transients.
On shared hosting with 2-4 PHP workers, one worker running cron means 25-50% of your capacity is gone. If cron takes 10 seconds to process a backlog of tasks, every visitor during that window competes for fewer workers.
2. The loopback can fail or hang
Some hosting configurations block loopback requests. Firewalls, misconfigured DNS, or SSL issues between your server and itself can make the “non-blocking” request actually block. I’ve seen sites where the cron loopback added 2-3 seconds to TTFB because the connection handshake timed out instead of completing instantly.
You can test this. Go to Tools > Site Health in your WordPress admin. If you see “Your site could not complete a loopback request” – that’s the same mechanism wp-cron uses. And it’s broken.
3. Plugin cron sprawl
Every plugin can register its own cron schedules. I’ve seen sites with 40+ scheduled cron events. Some plugins register tasks every 5 minutes. Others schedule one-off tasks and never clean them up, creating “orphaned” cron entries that pile up.
WordPress checks ALL of these on every page load. The more cron events exist, the more work that check does.
How to see what’s actually happening
Before you fix anything, diagnose the problem. Here’s how to see your cron situation clearly.
Using WP-CLI
If you have SSH access, WP-CLI gives you the best view:
# List all scheduled cron events
wp cron event list
# Count total events
wp cron event list --format=count
# See all registered schedules
wp cron schedule list
# Test if cron can run
wp cron test
The wp cron event list output shows you every scheduled task, when it’s due, and what schedule it’s on. Look for patterns – are there events scheduled every minute? Events that were supposed to run hours ago but haven’t? That’s your problem.
Check the cron option directly
All cron data lives in one autoloaded option. You can see how bloated it is:
# Check the size of your cron data
wp option get cron --format=json | wc -c
If that returns more than 10KB, you have cron bloat. I’ve seen sites where the cron option was over 100KB – serialized data loaded into memory on every single page load, whether cron runs or not.
Using the Query Monitor plugin
Install Query Monitor temporarily. It shows you exactly which cron events are registered and their schedules under the “Environment” panel. No SSH needed.
Find slow callbacks with WP Multitool
If you’re already using WP Multitool, the Find Slow Callbacks module helps identify which hooks – including cron hooks – are consuming the most execution time. Combined with the Slow Query Analyzer, you can trace performance issues back to specific cron tasks hitting the database hard.
The fix: disable wp-cron, use real cron
The solution is straightforward. Stop WordPress from checking cron on every page load, and use your server’s actual cron system to trigger it on a fixed schedule.
Step 1: Disable wp-cron in wp-config.php
Add this line to your wp-config.php, before the “That’s all, stop editing” comment:
define( 'DISABLE_WP_CRON', true );
This does exactly one thing: it tells WordPress to stop calling spawn_cron() on page loads. Your scheduled tasks still exist – they just won’t be triggered by visitors anymore. For a deeper dive into wp-config.php settings that affect performance, check out the complete wp-config.php performance tuning guide.
Step 2: Set up a real server cron job
SSH into your server and edit your crontab:
crontab -e
Add one of these lines. Option A uses wget:
*/15 * * * * wget -q -O /dev/null https://yourdomain.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1
Option B uses curl:
*/15 * * * * curl -s https://yourdomain.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1
Option C calls PHP directly – no HTTP overhead at all:
*/15 * * * * cd /path/to/wordpress && php wp-cron.php >/dev/null 2>&1
Option C is the best choice when possible. It skips the HTTP request entirely, which means no SSL handshake, no web server overhead, no loopback issues. The cron tasks run directly in PHP.
The */15 means “every 15 minutes.” That’s enough for most sites. If you publish scheduled posts and need them to go live on time, use */5 for every 5 minutes. Going below 5 minutes is rarely necessary.
Step 3: Verify it’s working
After setting up server cron, check that tasks are actually running:
# Manually trigger and watch output
wp cron event run --all --verbose
# Check if your cron job is in the crontab
crontab -l | grep wp-cron
Then check your site’s cron event list again after 15-30 minutes. The “next run” times should be updating, proving that server cron is triggering them correctly.
What about managed hosting?
Many managed WordPress hosts – WP Engine, Kinsta, Flywheel, Cloudways – already disable the default wp-cron and run it via server cron. Check your host’s documentation. Some handle this automatically. Others provide a UI to set cron intervals.
If you’re on shared hosting without SSH access, some control panels (cPanel, Plesk) let you set up cron jobs through the web interface. Look for “Cron Jobs” or “Scheduled Tasks” in your hosting panel. The command you’d add is the wget or curl version from above.
The ALTERNATE_WP_CRON option
There’s a middle ground that some people miss. WordPress has another constant:
define( 'ALTERNATE_WP_CRON', true );
This changes the execution model. Instead of spawning a loopback request, it runs cron tasks on the shutdown action – after the HTML has already been sent to the browser. The visitor doesn’t wait for cron, but it still runs on page loads.
This is useful when loopback requests fail (the Site Health error I mentioned earlier) but you can’t set up server cron. It’s not as clean as real server cron – you still need traffic to trigger tasks – but it eliminates the self-HTTP problem.
Cleaning up cron bloat
Switching to server cron fixes the triggering problem. But if you have dozens of orphaned cron events, they still add overhead when WordPress processes the cron option.
Here’s how to clean up:
# Find events from plugins you've already deactivated
wp cron event list --fields=hook,next_run,recurrence
# Delete a specific orphaned event
wp cron event delete <hook_name>
Look for hooks that reference plugins you no longer use. Common culprits include backup plugins that leave daily schedules behind, SEO plugins with hourly crawl checks, and analytics plugins pinging external APIs on a schedule.
While you’re cleaning up scheduled tasks, it’s worth checking your database for other types of bloat too. Old revisions, expired transients, and spam comments cause similar silent performance degradation.
The WooCommerce special case
WooCommerce deserves its own mention. It relies heavily on wp-cron for processing scheduled sales, sending emails, cleaning up sessions, and running the Action Scheduler – which itself can queue hundreds of tasks.
If you run WooCommerce and haven’t disabled wp-cron, you might see particularly bad spikes. The Action Scheduler can batch-process dozens of pending actions when cron triggers, occupying a PHP worker for 30+ seconds on busy stores.
The fix is the same – server cron – but consider running it more frequently (*/5 instead of */15) for WooCommerce stores to keep the Action Scheduler queue from backing up.
Monitoring after the fix
Once you’ve made the switch, here’s what to watch for:
- Consistent TTFB – The random spikes should disappear. If you were seeing 3-second outliers mixed with 400ms responses, they should level out.
- Scheduled posts publishing on time – The “Missed schedule” error on posts means cron isn’t running. If you see this, your server cron job isn’t configured correctly.
- Plugin features still working – Some plugins depend on frequent cron runs. If something stops working after you make the switch, check what cron interval it expects and adjust your server cron frequency.
If you’re still hunting down what’s making your WordPress site slow after fixing cron, the approach in optimizing WordPress for Core Web Vitals covers the other common bottlenecks – LCP, CLS, and INP.
The bottom line
WP-Cron is one of those things that works “well enough” on small sites with low traffic. But as your site grows, the cost of checking cron on every page load – and especially the self-HTTP loopback request – creates unpredictable performance that no amount of caching can fully fix.
The fix takes 5 minutes: one line in wp-config.php and one line in crontab. It’s one of the highest-impact, lowest-effort WordPress performance optimizations you can make. And unlike most performance tweaks, you’ll notice the difference immediately in your TTFB consistency.
Join developers and agency owners who get backend optimization strategies, tool releases, and deep-dive guides.
No spam. Unsubscribe anytime. We respect your privacy.
