Disable default WP cron job for performance (and do it the right way)
TL;DR: WordPress’ built‑in cron (WP‑Cron) runs on page loads. On busy sites it can add overhead; on quiet sites it can miss schedules. The fix is simple: disable the default trigger in wp-config.php and run wp-cron.php from a real scheduler (Linux cron, Windows Task Scheduler, or a third‑party ping) every 5–10 minutes. Verify with WP‑CLI and keep an eye on heavy jobs like WooCommerce’s Action Scheduler.
Why WP‑Cron hurts performance
WP‑Cron is a pseudo cron. It checks for due tasks on each page request and then tries to run them. That creates two common problems:
- High traffic: each request can spawn cron work, competing with real visitors for PHP workers and CPU. You’ll see extra concurrent requests to
/wp-cron.phpduring spikes. - Low traffic: jobs only run when someone visits, so “publish at 08:00” becomes “publish whenever the next person arrives.”
Typical symptoms: random slow spikes, scheduled posts missing their time, WooCommerce emails delayed, backups overlapping with peak traffic, or admin notices like “there was a problem spawning a scheduled task.”
When you should disable the default WP‑Cron trigger
You almost always benefit on production, especially if:
- You run WooCommerce or anything using Action Scheduler (marketing automations, imports/exports, webhooks). See WooCommerce’s Scheduled Actions docs.
- You have backups, feeds, or syncs scheduled.
- You use full‑page caching/CDN and want deterministic timings.
- You’ve seen missed schedule or spawn errors in the logs.
Local dev and tiny brochure sites can leave it as‑is, but standardising on a real cron keeps behaviour consistent across environments.
What “real cron” is (and how it differs from WP‑Cron)
A real cron is the operating system’s time‑based scheduler. On Linux and other UNIX‑like systems it’s provided by the cron daemon (crond). Jobs live in a crontab and run at the exact times you define – independent of web traffic, browsers, or PHP workers.
A minimal crontab entry looks like this:
# ┌ minute (0–59)
# │ ┌ hour (0–23)
# │ │ ┌ day of month (1–31)
# │ │ │ ┌ month (1–12)
# │ │ │ │ ┌ day of week (0–7, Sun=0 or 7)
# │ │ │ │ │
# * * * * * command to run
Windows offers a similar system via Task Scheduler, where you define a trigger (e.g., every 5 minutes) and an action (e.g., call wp-cron.php or run wp.exe cron event run --due-now).
Real cron vs WP‑Cron at a glance
- Trigger source: Real cron is driven by the OS clock; WP‑Cron is triggered on page load (an HTTP request).
- Timing accuracy: Real cron runs to the minute you set; WP‑Cron runs “whenever someone visits.” Quiet sites miss exact times; busy sites may spawn competing runs.
- Execution environment: Real cron usually runs CLI commands (PHP CLI, shell scripts, binaries) outside the web server and CDN. WP‑Cron executes inside a web request to
/wp-cron.php, inheriting web timeouts, WAF/CDN rules, and PHP‑FPM worker limits. - Resource isolation: Real cron jobs are separate processes and can be constrained/tuned (e.g.,
nice,ionice, systemd units, concurrency). WP‑Cron competes with front‑end PHP workers, causing spikes during traffic peaks. - Reliability/observability: Real cron integrates with system logging and exit codes; failures are easier to track and alert on. WP‑Cron failures can be silent until the next visitor or show “spawn” warnings in admin.
- Security and access: Real cron requires server access (or a trusted external pinger). WP‑Cron can be blocked by auth, firewalls, or cache layers.
- Multisite behaviour: One scheduler can cover a whole network with
wp cron event run --due-nowfrom the network root. With WP‑Cron, timing depends on visits across subsites.
Bottom line: WP‑Cron is a convenient fallback but not a scheduler. Production sites should disable its page‑load trigger and use a real scheduler to fire jobs predictably.
Step 1 – Disable WP‑Cron on page load
Edit wp-config.php and add this above the line that says “That’s all, stop editing! Happy publishing.”
// Disable the default WP‑Cron trigger (runs on page load)
define('DISABLE_WP_CRON', true);
This does not disable WordPress’ scheduling system itself; it only stops the automatic trigger on page views. You’ll provide your own trigger in the next step.
Pro tip: If you previously enabled the fallback
ALTERNATE_WP_CRON, remove it now to avoid odd redirects and?doing_wp_cronURLs.
Step 2 – Replace it with a real scheduler
Pick one of these options. The goal is simply to hit wp-cron.php on an interval.
A) Linux server (crontab)
Run every 5 minutes (safe default for most shops and blogs):
*/5 * * * * wget -q -O - https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1
Prefer curl?
*/5 * * * * curl -s https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1
Further reading: Kinsta’s walkthrough on disabling WP‑Cron and scheduling a system cron.
If you host multiple sites on one box, stagger them (e.g., */5 plus different minute offsets) to avoid bursting all jobs at once.
B) WP‑CLI (no HTTP request)
If you have SSH access, you can execute due events via WP‑CLI:
*/5 * * * * /usr/bin/wp --path=/var/www/example.com/public_html cron event run --due-now >/dev/null 2>&1
This runs jobs directly in PHP/CLI, bypassing web server constraints and some cache/CDN quirks. See WP‑CLI: wp cron event list, wp cron event run, and wp cron test.
C) Windows Task Scheduler
Create a Basic Task that runs every 5–10 minutes and executes:
powershell "Invoke-WebRequest https://example.com/wp-cron.php"
Reference: WordPress Plugin Handbook: Hooking WP‑Cron into the system task scheduler.
D) Third‑party ping service
If your host doesn’t expose cron, use a reliable external pinger (EasyCron, cron-job.org, etc.) to GET https://example.com/wp-cron.php?doing_wp_cron on your desired schedule.
Verifying it works (and monitoring)
Use WP‑CLI for instant feedback:
# List scheduled events
wp cron event list
# Manually run anything that is due now
wp cron event run --due-now
# Test the spawn system (helpful before you switch)
wp cron test
Docs: WP‑CLI – event list, event run, cron test.
What to look for:
- No growing backlog of overdue jobs.
- Heavy hooks (backups, imports, wc_*) finish within your interval.
- Scheduled posts/emails go out on time.
Gotchas and production notes
- Caching/CDN: Some page caches or security layers can block
wp-cron.php. Whitelist it and bypass full‑page cache for that path. If your CDN uses “Under Attack” modes, scheduled jobs may stall. - Basic/Auth or maintenance mode: If the site requires auth,
wp-cron.phpwill need credentials or a CLI approach. ?doing_wp_cronparameter: Totally fine to keep; it ensures the cron request isn’t cached and helps WordPress track a run. More info: Why is?doing_wp_cronbeing appended to my URLs?.- Multisite: One network‑level trigger is typically enough; subsites’ jobs will run when WordPress loads. If you rely on domain‑mapped subsites with separate traffic patterns, consider WP‑CLI’s
--due-nowfrom the network root. - Intervals: 5 minutes is a practical starting point. Low‑activity sites can push to 10–15 minutes; busy shops sometimes go to 1–2 minutes for time‑sensitive flows.
- Stagger the heavy stuff: If imports, backups, and marketing automations all line up at 00/05/10, move some to 02/07/12 to avoid spikes.
- Plugins that assume page‑load cron: Rare today, but if a plugin expects the page‑load trigger, keep an eye on it after switching.
Rollback
Need to revert quickly? Remove the DISABLE_WP_CRON line and delete your server task. WordPress will go back to page‑load triggering immediately.
FAQ
Will disabling WP‑Cron break scheduled posts?
No – provided you replace the trigger with a real scheduler. Scheduled posts (and everything else) keep working; they just run on your exact interval.
What frequency should I choose?
Start with every 5 minutes. If jobs routinely finish in seconds and you don’t need minute‑level precision, try 10–15 minutes. If you run a busy WooCommerce store with time‑sensitive automations, 1–2 minutes can be justified.
How do I see what’s actually scheduled?wp cron event list is the fastest way. For a GUI, the WP Crontrol plugin is solid on staging. On production, prefer WP‑CLI and logs.
Why do I still see ?doing_wp_cron in URLs?
You likely had ALTERNATE_WP_CRON enabled at some point; remove it. Some tools append it intentionally on cron pings – it’s harmless.
Next steps / Need a hand?
We’ve implemented this on high‑traffic blogs, WooCommerce shops, and content platforms. If you’d like us to audit and harden your WordPress performance – from cron to caching, PHP workers, and database autoloads – get in touch.
We’ve implemented this on high‑traffic blogs, WooCommerce shops, and content platforms. If you’d like us to audit and harden your WordPress performance – from cron to caching, PHP workers, and database autoloads – get in touch.
Related reading
- If you’re hunting down mystery slowdowns, check our article on whether deactivated plugins slow down your site.
- Image handling can also balloon CPU/IO. See our guide on disabling unnecessary WordPress image sizes and choosing the sizes you actually need.