Replace WP-Cron With a Real cPanel Cron Job
WP-Cron only runs when someone visits your site. Switch it to a real cPanel cron job so scheduled posts, backups and update checks run on time.
On this page
The short answer
WP-Cron only checks for due tasks when a page loads, so on a quiet site scheduled posts and update checks can run hours late. To fix it, create a cron job in cPanel that runs wget --delete-after https://example.com/wp-cron.php every 15 minutes, then add define( 'DISABLE_WP_CRON', true ); to wp-config.php. Back up wp-config.php first and confirm the job runs, because a system scheduler does not retry a missed run.
To replace WP-Cron with a real cron job, you add one line to wp-config.php that stops WordPress from running its scheduler on page loads, then create a cron job in cPanel that calls wp-cron.php on a fixed timetable, such as every 15 minutes. That is the whole idea, and it takes about 15 minutes to do safely.
Here is the surprising part. WordPress's own handbook says scheduling errors can happen "if you schedule a task for 2:00PM and no page loads occur until 5:00PM." Your scheduled post, your backup plugin and your update checks all wait for a visitor to show up.
By the end of this guide you will have WP-Cron switched off, a real cron job running it on time, a way to prove it works, and fixes for the problems people usually hit.
What WP-Cron actually is
WP-Cron is the small scheduler built into WordPress. According to the WordPress Plugin Handbook, it works "by checking, on every page load, a list of scheduled tasks to see what needs to be run." Core features such as publishing scheduled posts and checking for updates depend on it.
The important word is "page load". WP-Cron is not a background process. It only wakes up when someone (a visitor, you in the dashboard, or a bot) requests a page.
Think of it like a shop alarm clock that only rings when a customer opens the door. On a quiet Sunday it might ring hours late.
A real cron job is different. Cron is the time-based scheduler on Linux servers: you give it a timetable and a command, and it runs that command on schedule whether anyone visits your site or not.
Why this matters: a quiet site misses its schedule, and a very busy site does extra work, because WordPress checks the task list on every single request. Moving the trigger to a real cron job fixes both ends.
The honest trade-off
There is one downside, and the WordPress handbook names it clearly. With WP-Cron, a missed task is queued and runs on the next page load. With the system scheduler, "if the time passes and the task did not run, it will not be re-attempted."
So your cron job must keep running. If it stops, WordPress scheduling stops too, and nothing visible tells you.
Made-up example: Priya runs a small recipe blog on shared hosting. She schedules a post for 06:00 so it lands before breakfast traffic. Overnight nobody visits, so WP-Cron does not fire, and the post sits in "Scheduled" until her first reader arrives at 08:40. Her newsletter plugin, which waits for that post, also sends late. A cron job calling wp-cron.php every 15 minutes would have published it by 06:15 at the latest.
If you are new to how servers, control panels and accounts fit together, our plain-English guide to what web hosting is is a good primer before you start.
Before you start
This is a low-risk change, but you are editing the main WordPress config file. A typo in that file can take the whole site down until you fix it, so have a way back first.
- cPanel login for the hosting account that runs the site.
- Access to the Cron Jobs feature in cPanel. Your host can turn this interface on or off in WHM's Feature Manager, so some plans do not show it. If you cannot find it, ask your host before changing anything.
- A way to edit `wp-config.php`, such as cPanel File Manager, SFTP or SSH.
- A fresh copy of `wp-config.php` saved to your computer (Step 1 covers this).
- A full site backup if you have not taken one recently. The WordPress Advanced Administration Handbook says to practice regular backups and know how to restore them before modifying
wp-config.phpsettings. - Your exact site address, including
https://and whether it useswww. Example used below:https://example.com. - Optional: SSH and WP-CLI, if you want the WP-CLI method or command-line checks. WP-CLI is the official command-line tool for WordPress.
- Time: about 15 minutes, plus a short wait to confirm the first run.
Step by step: switch WP-Cron to a real cron job
The order matters. You create the cron job first, so there is never a gap where nothing triggers WordPress's scheduler.
Step 1: Back up wp-config.php
Open File Manager in cPanel and go to the folder where WordPress is installed. For a main domain this is often public_html, but it depends on your setup.
Right-click wp-config.php and download it to your computer. Keep that copy somewhere you can find in a hurry.
If you use SSH, this command makes a dated copy next to the original, so you can restore it in one move.
cp wp-config.php wp-config.php.bak-$(date +%Y%m%d)You should now see a file such as wp-config.php.bak-20261014 in the same folder.
Heads up: delete that .bak copy from the server once you have confirmed everything works. A config file with database credentials should not sit around in extra copies inside your web folder.
Step 2: Create the cron job in cPanel
Open Cron Jobs in cPanel. Menu layouts differ between cPanel themes and hosts, so the quickest way is to type "Cron" into the cPanel search box.
At the top you will see the Cron Email section. For your first test, enter an email address you read and click Update Email. The cPanel documentation says this is the address the system uses to send notifications when your cron jobs run. You will turn this off later.
Now go to the add form. cPanel requires you to fill in all six boxes: Minute, Hour, Day, Month, Weekday and Command. You can pick an interval from Common Settings to fill the first five for you, or type them yourself.
To run every 15 minutes, which is the example WordPress itself uses, enter these values.
Minute: */15
Hour: *
Day: *
Month: *
Weekday: *In cron syntax * means "any value" and */15 means "every 15th minute". So this runs at :00, :15, :30 and :45 of every hour.
Next, the command. This is the command from the WordPress Plugin Handbook, with your own address in place of the placeholder. It asks your site for wp-cron.php, and --delete-after tells wget to throw away the downloaded response so it does not pile up files in your home folder.
wget --delete-after https://example.com/wp-cron.phpPaste it into the Command box, replacing https://example.com with your real address, and click Add New Cron Job.
You should now see the new line in the Current Cron Jobs list, showing */15 * * * * and your command.
Why this matters: use the exact address your site lives on. If your site is https://www.example.com, use that, not http://example.com. Every redirect is one more thing that can fail quietly.
Quick tip: the cPanel docs ask you to "specify the absolute path to the command". If your host's cron cannot find wget, you will hear about it in the cron email after the first run. See Troubleshooting below for the fix.
Step 3: Choose an interval that fits your site
The WordPress handbook leaves the interval up to you: run it "on the intervals you desire". Fifteen minutes is the documented example and a sensible default for most blogs and small business sites.
Choose a shorter interval only if you need posts or tasks to land closer to their exact time. Keep in mind the cPanel warning: if you schedule jobs too often, the server could start a new run before the last one ends, and that duplication "may degrade performance."
Step 4: Turn off the page-load trigger in wp-config.php
Now that a real cron job is in place, tell WordPress to stop checking its task list on every page load.
Open wp-config.php in File Manager's editor (or your own editor over SFTP or SSH). Find the line that begins with /* That's all, stop editing! near the bottom of the file.
Add this line above that comment. It sets the DISABLE_WP_CRON constant, which is the setting WordPress documents for exactly this job.
define( 'DISABLE_WP_CRON', true );Placing it above the "stop editing" comment matters. Everything below that comment loads WordPress itself, and a setting added after that point is read too late.
Save the file. Then load your homepage and log in to wp-admin. You should see your site exactly as before. If you see a blank page or a PHP error instead, restore the backup from Step 1 right away and check for a missing quote, bracket or semicolon.
Heads up: check that DISABLE_WP_CRON is not already defined elsewhere in the file. Some hosts and plugins add it. Defining the same constant twice causes a PHP warning, so if it is already there, just make sure the value is true and skip adding a second line.
Step 5: Silence the cron email once it works
After you have seen one clean run (see the next section), you do not want an email every 15 minutes forever.
You have two choices, both from the cPanel docs. To stop mail for all cron jobs, remove the address from the Cron Email box and click Update Email. To stop it for just this job, click Edit next to it, add a redirect to the end of the command, and click Edit Line.
This version of the command sends both normal output and error output to /dev/null, which simply discards them.
wget --delete-after https://example.com/wp-cron.php >/dev/null 2>&1A note on that ending. The cPanel page shows it as /dev/null 2>&1, but the > in front is what actually redirects normal output in the shell. The order also matters: the GNU Bash manual explains that > file 2>&1 sends both outputs to the file, while the reverse order does not.
Alternative: run due events with WP-CLI instead of wget
If your host gives you SSH and WP-CLI, you can skip the web request entirely and have cron call WordPress directly. This avoids redirects, firewalls and HTTP problems, because nothing goes over the web.
The command is wp cron event run --due-now. The WP-CLI documentation says --due-now runs "all hooks due right now" and "respects the doing_cron transient to prevent overlapping runs". A transient is a small, short-lived value WordPress stores in its database, used here as a "busy" flag.
First, test it by hand over SSH. If you have not set up key-based SSH yet, our guide on SSH key login for cPanel hosting walks through it. From your WordPress folder, run this to execute everything that is due now.
wp cron event run --due-nowYou should see a line per event and then a summary like the one in the WP-CLI docs: Success: Executed a total of 2 cron events. Your number will differ, and it can be zero if nothing is due.
Next, find the full path to wp, because cron jobs should use absolute paths. This command prints it.
which wpNow build the cron command. The global --path flag tells WP-CLI where the WordPress files are ("Path to the WordPress files", in the WP-CLI handbook), and --quiet suppresses informational messages. Replace /path/to/wp with the output of which wp, and /home/USERNAME/public_html with your real WordPress folder.
/path/to/wp cron event run --due-now --path=/home/USERNAME/public_html --quietPut this in the Command box in Cron Jobs in cPanel with the same */15 * * * * timing, and do Step 4 as before.
Heads up: a few details from the docs worth knowing. The --exclude=<hooks> option, which skips named hooks, was added in WP-CLI 2.8.0. The --network option for multisite is marked as "not part of a stable WP-CLI release yet" at the time of writing (14 October 2026), so do not rely on it in a cron job unless you run the nightly build.
How to check it worked
Do not trust a cron job you have not watched run. Here are the checks, roughly from quickest to most convincing.
Check 1: Request wp-cron.php yourself
From your own computer or over SSH, this asks for the headers of wp-cron.php, the same file your cron job calls.
curl -I https://example.com/wp-cron.phpYou want an HTTP status line in the 200 range near the top. A 301 or 302 means your cron URL is not the final address, so update the command to the address in the Location header. A 403, 429 or 5xx means something (a security plugin, firewall or the server) is blocking or failing the request.
Check 2: Read the first cron email
Wait for the next 15-minute mark plus a minute or two. With Cron Email still switched on, you should receive a message for the run. Read it: an error such as wget not being found, a failed connection or a 403 shows up here.
Check 3: Watch the task list move
If you have WP-CLI, this lists scheduled events with their next run time. By default it shows the hook name, next run in GMT, how long until it runs, and how often it repeats.
wp cron event listRun it, wait for your cron job to fire, and run it again. Events that were due should now show a new next run time in the future. Events that sit in the past and never move tell you the trigger is not working.
Check 4: Schedule a test post
Create a draft post called "Cron test", set it to publish 20 minutes from now, and do not visit the site in that time. Use a private browser window afterwards, or check from your phone on mobile data, to see whether it published. Then delete the test post.
Check 5: Expect wp cron test to complain
If you run wp cron test after Step 4, it will report an error. That is normal. The WP-CLI docs say the command checks whether DISABLE_WP_CRON is set and "errors if true because WP-Cron is disabled." It tests the page-load spawning you just turned off, not your new cron job.
Troubleshooting
Scheduled posts still publish late
Symptom: posts stay in "Scheduled" past their time. Likely cause: the cron job is not running, or it is calling the wrong address. Fix: check the cron email for errors, run Check 1 with the exact URL from your command, and confirm the job still appears in Current Cron Jobs.
The cron email says wget was not found
Symptom: each run sends a message saying the shell could not find wget. Likely cause: the cron environment has a shorter search path than your SSH session, or wget is not installed on that server. Fix: over SSH, run which wget and use the full path it prints at the start of the command. If wget is not installed, use the WP-CLI method, or ask your host which tool they support for cron requests.
The site shows a blank page after editing wp-config.php
Symptom: white screen or a PHP parse error right after Step 4. Likely cause: a typo, such as curly quotes from a word processor, a missing semicolon, or the line pasted inside another block. Fix: restore your backup copy, then add the line again by typing it in a plain text editor. Make sure it uses straight single quotes.
The cron job gets a 403 or 503 response
Symptom: the cron email or Check 1 shows 403 Forbidden or a 5xx error. Likely cause: a security plugin, web application firewall or bot protection is blocking automated requests to wp-cron.php. Fix: check the plugin or firewall logs for blocked requests to that path and allow them, or switch to the WP-CLI method, which does not use HTTP at all.
You get an email every 15 minutes
Symptom: your inbox fills with cron messages. Likely cause: Cron Email is still set and the command has no redirect. Fix: follow Step 5. Either clear the Cron Email address and click Update Email, or add >/dev/null 2>&1 to the end of this one job.
Tasks seem to run twice or the server slows down
Symptom: duplicate emails from a plugin, or load spikes every few minutes. Likely cause: two triggers (for example, two cron jobs for the same site, or an interval so short that runs overlap). Fix: make sure there is only one job per site in Current Cron Jobs and lengthen the interval. With WP-CLI, --due-now respects the doing_cron lock. WordPress also documents a WP_CRON_LOCK_TIMEOUT constant, which makes sure a cron process "cannot run more than once every" that many seconds.
The Cron Jobs icon is missing in cPanel
Symptom: you cannot find Cron Jobs anywhere, even with the search box. Likely cause: your host has disabled the feature for your package in WHM's Feature Manager. Fix: ask your host to enable it. Do not add DISABLE_WP_CRON until you have a working cron job, or scheduling will stop completely.
A few pro tips and safety notes
When you migrate the site to a new server, the DISABLE_WP_CRON line travels with wp-config.php, but the cron job does not. Recreate the cron job on the new host on day one, or remove the line until you do.
On WordPress multisite, each site in the network has its own schedule. The WP-CLI --url flag lets you target one site ("Pretend request came from given URL"), so you may need one cron line per site.
Update checks run through the same scheduler. Once your cron job is in place, keeping WordPress patched gets a little more predictable; our post on the WordPress 7.1.3 security release shows how to confirm your site is current.
Ask your host or a developer for help if your site sits behind a firewall service you do not control, if you run multisite, or if a plugin depends on very precise timing, such as bookings or stock sync.
The one message worth forwarding
Copy this and send it to whoever looks after the site:
"Hi, I am moving our WordPress site from WP-Cron to a real cron job so scheduled posts and updates run on time even with no visitors. The plan: add a cPanel cron job running every 15 minutes with wget --delete-after https://example.com/wp-cron.php >/dev/null 2>&1, then add define( 'DISABLE_WP_CRON', true ); above the stop-editing comment in wp-config.php. I will back up wp-config.php first and test with a scheduled post. If we ever migrate hosts, the cron job has to be recreated, or scheduling stops. Any objections before I do it?"
The takeaway: a real cron job makes WordPress run on the clock instead of on visitors, as long as you check that the job keeps running.
Reader questions
Is it safe to disable WP-Cron?
Yes, as long as something else triggers wp-cron.php on a schedule. If you disable it without adding a real cron job, scheduled posts, update checks and plugin tasks stop running.
How often should my cron job call wp-cron.php?
Every 15 minutes is the example WordPress uses and suits most sites. Go shorter only if tasks must land closer to their exact time, and avoid intervals so short that runs overlap.
Can I use curl instead of wget?
The official WordPress handbook only documents wget for Linux cron. If your host does not have wget, the WP-CLI command wp cron event run --due-now is a documented alternative that avoids web requests.
Why does wp cron test report an error after I disable WP-Cron?
The command checks whether DISABLE_WP_CRON is set and errors if it is true. That is expected, because it tests the page-load trigger you switched off, not your new cron job.
What happens if my cron job misses a run?
WordPress docs say that with the system scheduler, a task whose time passes without running will not be re-attempted. That is why you should confirm the cron job keeps running, especially after plan changes or migrations.
My hosting plan has no Cron Jobs option. What can I do?
Hosts can switch the Cron Jobs interface off in WHM Feature Manager. Ask your host to enable it, and keep WP-Cron on until you have a working cron job.
Sources & further reading
- WordPress Plugin Handbook: Cron
- WordPress Plugin Handbook: Hooking WP-Cron Into the System Task Scheduler
- WordPress Advanced Administration: wp-config.php
- cPanel Docs: Cron Jobs
- WP-CLI: wp cron event run
- WP-CLI: wp cron event list
- WP-CLI: wp cron test
- WP-CLI Handbook: Global parameters and config
- GNU Bash Manual: Redirections
- GNU Wget Manual
Originally published . About our editorial updates.


