Skip to content

WordPress notes

External cron jobs for WordPress, the right way

By Hannah Voss, Editor · Reviewed September 2026 · 6 min read

Quick answer

WP-Cron runs scheduled tasks only when someone visits the site, so on quiet or fully cached sites tasks run late or not at all. Set DISABLE_WP_CRON to true in wp-config.php, then have your server or host call wp-cron.php, or run WP-CLI's cron command, every five to fifteen minutes. Check the schedule with WP-CLI afterwards.

A pale paper clock face without numerals and with a single graphite hand, linked by a paper strip to a small cog

Why WP-Cron misses tasks

WordPress has no background process of its own. On each page load it checks whether any scheduled task is due and, if so, starts it. On a busy site that works. On a quiet site, or one where a page cache answers most visits before WordPress runs, due tasks wait until the next uncached visit. Share count refreshes, scheduled posts, backups and email digests all depend on this. The old post on this domain about using external cron jobs was written because share counts stopped updating for exactly this reason.

Why a cached, quiet site misses WP-Cron tasks01Visitor arrivescached page02WordPress not runno cron check03Tasks waitcounts go stale
A real cron job removes the dependency on visitors.

A worked example with numbers

The figures are invented to show the arithmetic. A small magazine gets 400 page views a day. A page cache serves 97 percent of them, so WordPress itself runs for about 12 requests a day, and those cluster around the two times the editor logs in. A share plugin schedules its count refresh every five minutes, which was the old plugin's default "Cache expiration", and a backup plugin schedules a nightly export at 03:00.

With WP-Cron alone, the refresh runs at most 12 times a day, at whatever moment a request happens to arrive, so counts on the homepage are between one and fourteen hours old. The nightly backup runs at 03:00 only if someone hits an uncached page after 03:00, so it runs at 09:15 when the editor logs in, during business traffic. With a server cron job every ten minutes, WordPress is called 144 times a day at fixed times: the count refresh is never more than ten minutes late and the backup starts within ten minutes of 03:00. The cost is 144 short requests a day, which even a small host tolerates. The two lines that make the change are these.

// wp-config.php, above the line that says to stop editing
define( 'DISABLE_WP_CRON', true );

# crontab -e: every 10 minutes, quietly
*/10 * * * * curl -s -o /dev/null https://example.com/wp-cron.php?doing_wp_cron

Then confirm the schedule moves. The listing below is what a healthy site shows a few minutes after the first cron run: every next-run time is in the future and the share refresh hook has a short interval.

$ wp cron event list --fields=hook,next_run_relative,recurrence
+------------------------------+------------------+------------+
| hook                         | next_run_relative| recurrence |
+------------------------------+------------------+------------+
| example_share_refresh        | 3 minutes        | 5 minutes  |
| example_backup_nightly       | 9 hours 40 min   | 1 day      |
| wp_version_check             | 6 hours          | 12 hours   |
+------------------------------+------------------+------------+

Set up a real cron job

  1. Disable the visit trigger

    Add define( 'DISABLE_WP_CRON', true ); to wp-config.php, above the line that says to stop editing.

  2. Choose a schedule

    Every 5 to 15 minutes suits most sites. More often wastes resources.

  3. Add the job

    In your host's cron panel or crontab, call the cron endpoint or WP-CLI (examples below).

  4. Verify

    Run wp cron event list and confirm the next run times move forward.

# crontab: call wp-cron.php every 15 minutes
*/15 * * * * curl -s https://example.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1

# or, with shell access, use WP-CLI
*/15 * * * * cd /path/to/site && wp cron event run --due-now > /dev/null 2>&1

Choosing between the two

MethodProsCons
HTTP call to wp-cron.phpWorks on any host with a cron panelGoes through the web server and its timeouts
WP-CLI on the serverNo web timeouts, runs as a processNeeds shell access and WP-CLI
Host's built-in schedulerNo setupCheck the interval it uses

Check tasks are healthy

  • wp cron event list shows every scheduled hook and when it next runs.
  • Hooks with next-run times in the past mean cron is not running.
  • Site Health in the dashboard warns when scheduled events are late.

Symptoms, causes and fixes

SymptomCauseFix
Scheduled posts show "Missed schedule"No request reached WordPress at publish timeAdd a server cron job; the post publishes on the next run
Share counts stale for daysThe refresh hook waits for an uncached visitServer cron every 5 to 15 minutes; see share counts not updating
Next-run times in the past after adding cronDISABLE_WP_CRON is set but the job never firesCheck the crontab saved, the URL is right, and the firewall allows it
Job runs but tasks still lateThe HTTP call times out before long tasks finishSwitch to WP-CLI, or raise the timeout on the curl call
Two runs overlap and duplicate emailsInterval shorter than the longest taskLengthen the interval, or use WP-CLI which waits for completion
Site Health reports a loopback errorThe server cannot request its own addressUse WP-CLI, which needs no loopback

Pitfalls with a WordPress cron job

  • Placing the constant below the "stop editing" line in wp-config.php. Constants defined there are read too late. Put it above.
  • Forgetting ?doing_wp_cron on the URL. Without it, a page cache or the cron lock may answer the request without running tasks.
  • Running the job as a user who cannot read the site. WP-CLI needs the same PHP version and file permissions as the web server; a job that runs as root creates files the web server cannot later overwrite.
  • Setting a one-minute interval. Most hooks recur every five minutes or longer, so extra runs only add load, and a long task can overlap the next run.
  • Leaving ALTERNATE_WP_CRON on from an old fix. It redirects visitors to trigger cron and is unnecessary once a real job exists.
  • Assuming the host's scheduler calls WordPress. Some call a file path with PHP directly, which loads nothing; check its command.

Once the schedule is reliable, the remaining share count problems are almost always tokens or caching, which the share counts section covers. Before blaming a plugin for anything else, work through the plugin troubleshooting order, and if an update has started raising deprecation notices, fixing the get_currentuserinfo() is deprecated notice shows the pattern. All three sit in the WordPress troubleshooting notes.

Sources

Questions

Should I disable WP-Cron?

On low-traffic or heavily cached sites, yes, as long as you replace it with a real cron job.

How often should WordPress cron run?

Every 5 to 15 minutes is enough for most sites.

How do I know cron is working?

Run wp cron event list and check that next-run times are in the future and advancing.

Does DISABLE_WP_CRON stop scheduled posts from publishing?

It stops visits from triggering the schedule, so without a server cron job scheduled posts miss their time. With the job in place they publish on the first run after the scheduled minute.

Can I use wget instead of curl?

Yes. wget -q -O /dev/null followed by the same wp-cron.php address does the same job. What matters is that something requests the endpoint on a fixed interval.

Share this guide

Hannah Voss, Editor. Checks every guide against a working WordPress install and the networks' current documentation. Last reviewed September 2026.

  1. Share count not updating or not accurate: a fix listShare counts stuck at zero or wrong? Work through tokens, caches, cron, URL variants and rate limits in order, and why you should never add fake shares.6 min read
  2. Plugin troubleshooting: what to try firstTroubleshoot a WordPress plugin problem in a safe order: caches, conflicts, debug logging, recovery mode, and disabling a plugin when the admin is down.6 min read
  3. Fixing the get_currentuserinfo() is deprecated noticeWhat the get_currentuserinfo() is deprecated notice means, how to find the plugin or theme calling it, and the one-line fix using wp_get_current_user().6 min read