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.