Use a real cron job instead of the default WordPress WP-Cron (for better performance and reliability).
- What is WP-Cron, what is it used for, and how to use a Linux server cron job instead.
I promise this is all very easy to do and totally worth your time. It might also fix other random issues you’ve had on your site.
ALWAYS use a real cron job instead of WP-cron.
STEP #1 – disable WP-cron from your wp-config.php file
- Open up wp-config.php
define( 'DISABLE_WP_CRON', true);anywhere above the line that says, “That’s all, stop editing! Happy blogging.”
STEP #2 – create a cron job from your webhosting control panel
- Log into your webhosting control panel (cPanel, etc) and find the Cron Jobs function.
- Add this line and set it to 5 min intervals
wget -q -O - https://domain.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1(change the domain to yours)
- Some hosts may have limits and force you to use longer intervals (30 mins and up). It’s fine, use the lowest one you can. Even if it’s your own server, I think 5 or 10 mins is frequent enough.
- If you really need a higher frequency than what your webhost allows, you can either A) get your own server where you have no limits, or B) get a 3rd-party cron service like EasyCron or even Cloudflare workers.
- Some guides out there use the server directory path (
/home/user/public_html/wp-cron.php?doing_wp_cron) instead of the URL. I prefer the domain version as it’s easier to understand and safer (since actual server directory might be different). I think only benefit for server path version is it’s slightly less server work not having DNS lookups and SSL handshake but it’s not noticeable at all.
- You can use WP Crontrol plugin to manage your cron jobs if you feel they’re backed up or stuck. It’s sometimes an issue for bloated sites. If your cron jobs haven’t run for a while, your site may seem slow or crashed while it catches up. Just wait 5-10 minutes and it should work again.
- Multi-sites only have to set the cron job for the main site domain. You don’t have to set for each site in there.
You’re done here. Nothing else to do! If if you want to learn more about how cron jobs work, keep reading…
What is a cron job?
A “cron job” is a service built into all Linux servers that runs processes at a scheduled time. (Sometimes called server cron, linux cron, system cron, cron job, “real cron job”.)
These processes are listed in the crontab file on the server. (Usually located in
/var/spool/cron for CentOS/RHEL and
/var/spool/cron/crontabs/ for Ubuntu/Debian.)
For those curious, the crontab file usually looks like:
0 6 * * * /usr/local/cpanel/scripts/exim_tidydb > /dev/null 2>&1 30 5 * * * /usr/local/cpanel/scripts/optimize_eximstats > /dev/null 2>&1 14 21 * * * /usr/local/cpanel/whostmgr/docroot/cgi/cpaddons_report.pl --notify 32 0 * * * (/usr/local/cpanel/scripts/fix-cpanel-perl; /usr/local/cpanel/scripts/upcp --cron > /dev/null) 0 2 * * * /usr/local/cpanel/bin/backup 35 * * * * /usr/bin/test -x /usr/local/cpanel/bin/tail-check && /usr/local/cpanel/bin/tail-check 5,20,35,50 * * * * /usr/local/cpanel/scripts/eximstats_spam_check 2>&1 /usr/local/cpanel/scripts/update_mailman_cache && /usr/local/cpanel/scripts/update_db_cache 25 */2 * * * /usr/local/cpanel/bin/mysqluserstore >/dev/null 2>&1 15 */2 * * * /usr/local/cpanel/bin/dbindex >/dev/null 2>&1 15 */6 * * * /usr/local/cpanel/scripts/autorepair recoverymgmt >/dev/null 2>&1 */5 * * * * /usr/local/cpanel/scripts/dcpumon-wrapper >/dev/null 2>&1 12,27,42,57 * * * * /usr/local/cpanel/whostmgr/bin/dnsqueue > /dev/null 2>&1 22 22 * * 7 /usr/local/cpanel/scripts/send_api_notifications > /dev/null 2>&1
I know all that looks scary right now but don’t worry! They’re just typical server scheduled server commands that tell it to do maintenance checks and tasks. For example:
- Running daily backups.
- Sending out notifications.
- Deleting mailbox trash that’s older than 30 days.
- Restarting failed services.
Basically all automated tasks are run by the server cron. It simply checks all the time and runs tasks when they’re scheduled. Most of the tasks you see in there are automatically entered by your server. But you can also add your own (sometimes required for certain plugin functions).
How to read a cron command:
15 */2 * * * /usr/local/cpanel/bin/dbindex >/dev/null 2>&1
15 */2 * * *is the interval part. I would guess this means 15 mins on the hour of every other hour. (e.g. 2:15, 4:15, 6:15)
/usr/local/cpanel/bin/dbindexis the command. Basically if you had to type this command manually in CLI everyday, you can now simply make it a cron job and you wouldn’t have to do it anymore. It will run exactly as you type.
>/dev/null 2>&1tells the system to discard any errors or outputs, instead of sending them to an error log or emailing you. Useful since this is only an automated task and nobody is actually at the computer to read it anyway.
And what is WP-Cron?
WP-Cron is PHP function built into WordPress that simulates the server cron service.
It doesn’t actually check the server crontab file every second and run its scheduled tasks. It has its own internal “cron handler” file (not actually a file but stored in database) and only checks it when someone loads up the website. Basically, it’s a fake or pseudo cron service. Or as some developers like to say…”not a real cron job”.
Some scheduled “automated” functions you might find in a typical WordPress site:
- Comments automatically emailed to commenters.
- Backup plugin running every night.
- Scheduled posts publishing live when it’s time.
- Widgets updating to show the latest data.
How do you think your site “automatically” handles these functions? It does it using the WP-Cron function (which is built into WordPress and runs from the wp-cron.php file). And is triggered every time the website is requested.
This means…every time someone (or a crawler) visits your website…your site runs the wp-cron.php file.
….this can be bad for 2 main reasons:
- If your site has MANY VISITS (over 2K/day):
- It slows down the server needlessly checking the WP cron list multiple times every minute.
- So a high-traffic site with 500 visitors per second would run 500 WP-cron runs every second, not even including typical bot traffic as well!
- If your site has NO VISITS (under 100/day):
- Your scheduled tasks might not run for long periods. For example if no one (no person or even a bot) visits your site, and you don’t log into the wp-admin area…your backup might not run, etc.
- The scheduled tasks pile up until the moment you visit and then it runs super slow on that visit since it’s busy offloading all the backed up wp-cron tasks.
In real-world practice, the first issue is more common for me. It’s that high-traffic sites are being slowed down by excessive WP-cron runs. The latter issue of low-traffic sites doesn’t matter much because low-traffic usually means low importance anyway. You should absolutely disable it WP-cron and use your Linux server cron jobs instead if you have more than 100k monthly visitors.
Thanks for another helpful post for us server newbies. Will try it on my low-traffic sites.
When you advise this command: wget -q -O – https://domain.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1
Could anyone in theory run wget -q -O – https://wpjohnny.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1 …
… and trigger your WP cron from a different server? Seems odd if they could.
Yes, anybody can trigger the cron from a remote server. That could be for using external cron service to get around local server cron limitations. Or could also be someone trying to DDoS on wp-cron call. There’s nothing odd about it. Public-facing applications have open services.
Thank you for sharing!
Hi Johnny, Thanks for this tip.
Just a question regarding shared hosting account using Addon Domains. You mentioned with Multi-sites that you do not need to add a Cron job for each site, but how about Addon Domains?
If I setup a Cron job on the Primary domain do I still need to add Cron jobs for each of the Addon Domains too? I would think yes, but would like clarification please.
If those addon domains are part of the same multi-site, then NO you don’t have to add cron jobs for each.
No – not using multi-site. So yes I’ll add a Cron job for each Addon site as they are only connected by being on the same Hosting account. Thanks for letting me know.
Sorry one more question… If I setup a Cron job on a website, but the website has subdomains – will I need to setup a Cron job for each subdomain? Or will the Cron job on the main root domain run for all subdomains?
You don’t need the ?doing_wp_cron GET param; as far as I can tell it’s only added by a cron problem workaround called ALTERNATIVE_WP_CRON (defined in wp-config.php). Adding it causes confusion.
I’m pretty sure that wp-cron.php doesn’t actually run for each request, it only runs if there is a cron request scheduled. However, if the page is cached, cron might not run at all. In this case the switch to system cron is important for reliability, and to ensure that some pages don’t get delayed. It’s considered best practice to run cron from the site crontab for any reasonably active site, regardless.
A very useful guide, thank you.
What is the difference between the approach you describe:
wget -q -O – https://domain.com/wp-cron.php?doing_wp_cron
and the approach described on sites like namecheap:
/usr/local/bin/php -q /home/usrname/public_html/wp-cron.php
Calling the domain (my approach) is technically easier and foolproof since there’s less chance for newbies to mess up the command. As you can see, they only need to know their domain name instead of having to know where their website directory is on the server. For example RHEL distros tend to have “/home/user/public_html”, and Debian distros tend to have “/var/www/html/”. Good luck finding a newbie who knows what their site directory path is…and especially if their site moved across different servers over time.
Calling the local server file (more “proper” approach) is probably faster, uses fewer server resources, and less likely to get blocked by WAF security since it calls the local file directly. But you have to know the full path to your website directory.
Imagine like one employee calling another from the same building. He or she could call using the public number (1-800-WATEVER, EXT 123), or he/she could simply call using the internal extension number (EXT 123). Obviously, calling internally would be faster and more direct (instead of having it route outside and then back in).
Excellent. Thanks, Johnny.
Would you happen to now why a CRONs next run would be scheduled 51 years in the past?
My site doesn’t want to run CRON jobs anymore even after i followed you excellent advice.
HOOK – wp_site_health_scheduled_check
ARGUMENTS – None
NEXT RUN – 1970-01-01 01:00:01 +01:00 51 years 11 months ago
ACTION – WP_Site_Health->wp_cron_scheduled_check()
RECURRENCE – Non-repeating
Hi Johnny! I have multisite with sub directory. What I can see is that I have to set a new cron job for every sub site I have. I installed “wp control” and my WordPress cron are working for my main site, all of my other sub sites doesn’t trigger when it’s time to do that! They are stuck. I know that multisite share the same WordPress installation and database but they have there own different setup in WordPress. Different active plugins and also woocommerce that’s unique on every sub site. Can this be right ? Am i missing something here? I have 25 sub sites so that would be 25 cron jobs I have to activate.
What is different between “wget -q -O – https://domain.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1″ and “wget -q -O – https://domain.com/wp-cron.php?doing_wp_cron“?
You’re asking about the “> dev/null 2>&1” part? It’s to discard the output, instead of displaying on screen.
I appreciate so much your advice. Can you help with this request? (click below)
Is 5 min enough to run wp-cron or should I run wp-cron after every 10 or 15 min?
That’s your choice to make. It depends on what you need it for. But generally, 5 mins should be a small enough interval.