गाइड · वर्डप्रेस

WP-Cron Explained: Fix Missed Schedules and Switch to a Real Cron Job

How WordPress WP-Cron works, why scheduled posts and tasks get missed, how to set DISABLE_WP_CRON with a real system cron, and how to monitor it with heartbeats.

लेखक: SiteDownCheck संपादकीय टीम · अपडेट: अक्टूबर 5, 2026 · 8 मिनट पढ़ने में

WordPress has no scheduler of its own that runs in the background. Scheduled posts, plugin update checks, backups, WooCommerce subscription renewals, email queues and cache cleanup all rely on WP-Cron, a pseudo-cron that runs on web requests. It works well enough on busy sites and fails in quiet ways on others: "Missed schedule" posts, backups that do not run, emails sent hours late, or a slow request because a heavy task ran during a page load.

This guide explains how WP-Cron works, how to diagnose problems, and how to replace it with a real cron job, which is the right setup for most sites that care about timing.

How WP-Cron works

WP-Cron is triggered by visitors. On a normal page load, WordPress checks whether any scheduled event is due. If so, it sends a non-blocking HTTP request to wp-cron.php on your own site, and that file runs the due events. Events are stored in the cron option in the database, each with a hook name, a timestamp and optionally a recurrence such as hourly, twicedaily or daily.

This design has consequences:

  • No traffic means no cron. On a site with a few visits a day, a task due at 03:00 runs when the next visitor arrives. Scheduled posts show "Missed schedule" for that reason.
  • Precision is poor. Events run "at or after" their time, not at it.
  • A heavy task may delay a visitor's request if the cron request runs in the same process or exhausts workers.
  • Many page views trigger many cron checks. Each tries to start wp-cron.php. WordPress uses a short lock (60 seconds) to prevent overlap, but on busy sites you still pay for needless requests.
  • It depends on loopback. The site must be able to request its own URL. If the server blocks it, WP-Cron silently never runs.

Check whether WP-Cron is healthy

With WP-CLI:

wp cron event list
wp cron schedule list
wp cron test

wp cron event list shows each hook, its next run time and recurrence. If events show next-run times far in the past (for example, yesterday), cron is not running. wp cron test tries a spawn of wp-cron.php and reports whether it works.

To run everything that is due right now:

wp cron event run --due-now

The Site Health screen (Tools, Site Health) also reports failures, such as "The scheduler is not running" or loopback request problems, in newer WordPress versions.

You can also request the cron endpoint yourself and look at the response:

curl -s -o /dev/null -w "%{http_code} %{time_total}s\n" "https://example.com/wp-cron.php?doing_wp_cron"

A 200 with an empty body is normal. A 403, 401 or redirect means a firewall, basic auth or an HTTPS redirect is interfering. You can confirm the status with the HTTP स्टेटस चेकर.

Common reasons WP-Cron fails

CauseWhat happensFix
Very low trafficTasks run lateUse a real cron job
Caching serves all pagesVisits never reach PHP, so nothing triggers cronReal cron job
HTTP basic auth or "maintenance" plugin on the siteLoopback gets a 401 or 503Allow the server's own IP, or use CLI cron
Security plugin or WAF blocks wp-cron.phpRequests are denied (often 403)Allow it, or use CLI cron
Redirect loops or wrong home URLCron request never reaches the fileFix URLs; see too many redirects
DNS or SSL problems for loopbackThe server cannot resolve or trust its own hostnameFix DNS or certificate; add a hosts entry
DISABLE_WP_CRON set with no replacementNothing runs at allAdd a system cron
Fatal error in a cron callbackThe event fails every timeCheck the debug log; see the white screen guide
Long-running job hits PHP time or memory limitJob is killed halfwayRun via CLI, raise limits; see memory limit

Switch to a real cron job

The standard fix is two steps: turn off the visitor-triggered mechanism, and run wp-cron.php or WP-CLI on a fixed schedule from the operating system.

Step 1: Disable the built-in trigger

In wp-config.php, above the "stop editing" line:

define( 'DISABLE_WP_CRON', true );

Step 2: Add a system cron entry

Option A: WP-CLI (preferred, runs in PHP without the web server). Edit the crontab of the user that owns the site:

crontab -e
*/5 * * * * cd /var/www/html && /usr/local/bin/wp cron event run --due-now --quiet >/dev/null 2>&1

Use the full path to wp (check with which wp), run it as the site user (not root, or add --allow-root and fix file ownership afterwards), and make sure the wp command works from cron's minimal environment. Running through the CLI avoids web server timeouts and the loopback problem entirely.

Option B: Request wp-cron.php over HTTP, useful on shared hosting where only URL-based cron is available:

*/5 * * * * curl -s -o /dev/null "https://example.com/wp-cron.php?doing_wp_cron"

or with wget -q -O - "https://example.com/wp-cron.php?doing_wp_cron" >/dev/null 2>&1. In cPanel, add it under Cron Jobs; on Plesk, under Scheduled Tasks. Some hosts offer a graphical "WordPress cron" toggle that does the same.

Interval: every 5 minutes suits most sites. Every minute is reasonable for busy stores with time-sensitive jobs (subscription renewals, email queues), and every 15 minutes is fine for a small blog. Scheduled posts will publish at most one interval late.

Avoid overlap: if a run can take longer than the interval, wrap the command in flock:

*/5 * * * * flock -n /tmp/wpcron.lock -c 'cd /var/www/html && wp cron event run --due-now --quiet'

For multisite, use wp site list --field=url | xargs -I{} wp cron event run --due-now --url={} or schedule one cron per site.

Step 3: Verify

Wait a few intervals, then check:

wp cron event list --fields=hook,next_run_relative

Next-run times should be in the near future, not the past. Publish a test post scheduled five minutes ahead and confirm it goes live.

Alternate: keep WP-Cron but make it more reliable

If you cannot use a real cron (some locked-down hosts), keep WP-Cron enabled and:

  • Make sure the site can reach itself: loopback must not be blocked by auth, a firewall or bad DNS. Test from the server with curl -I https://example.com/wp-cron.php.
  • Set ALTERNATE_WP_CRON to true only as a last resort; it uses a redirect-based method that has side effects.
  • Use an external service to request wp-cron.php every few minutes, which at least guarantees regular triggers.

Monitor cron with heartbeats

A cron job that silently stops is worse than one that fails loudly, because nobody notices until the damage is done: no backups for a month, no orders processed. The fix is heartbeat monitoring (sometimes called dead man's switch monitoring). Your job pings a unique URL every time it finishes. If no ping arrives within the expected window, you get an alert.

With SiteDownCheck's cron monitoring you create a heartbeat monitor and append the ping to the cron line, so it fires only when the command succeeded:

*/5 * * * * cd /var/www/html && wp cron event run --due-now --quiet && curl -fsS -m 10 --retry 3 "https://YOUR-HEARTBEAT-URL" >/dev/null

The && means no ping on failure, so a broken run triggers an alert after the grace period. Set the expected period to match the schedule (5 minutes) with a grace window of a few minutes. See the cron job heartbeat monitoring guide for patterns such as start and finish pings and choosing grace periods.

Also keep ordinary uptime monitoring on the site itself. The two are complementary: heartbeats catch silent background failures, uptime checks catch the site being unreachable.

Troubleshooting a specific stuck event

wp cron event list --hook=my_hook         # find the event
wp cron event run my_hook                 # run it now and see errors
wp cron event delete my_hook              # remove a broken or duplicate event
wp cron event schedule my_hook now hourly # reschedule it

Duplicate events can build up when a plugin schedules the same hook on every request. Deleting extras and fixing or removing the plugin stops the growth. A huge cron option (hundreds of entries) slows every request, since WordPress loads and checks it constantly.

If an event runs but does nothing, check the plugin's own logs, and turn on WP_DEBUG_LOG to capture fatal errors that happen in the cron context. A background job that works from the command line but not on the web usually points to different PHP limits or environment between CLI and web.

अगले कदम

सामान्य प्रश्न

Is it safe to disable WP-Cron?

Yes, as long as you replace it with a system cron that runs wp cron event run --due-now or requests wp-cron.php regularly. Disabling it without a replacement stops every scheduled task.

How often should the real cron run?

Every 5 minutes works for most sites. Use every minute for stores or sites with time-critical jobs. The interval is the maximum delay for any scheduled event.

Why do my scheduled posts show "Missed schedule"?

The scheduler did not run at the post's time, typically because of low traffic, page caching that prevents PHP from running, or a blocked loopback request. A real cron job fixes it, and you can publish the stuck posts by running wp cron event run --due-now.

Will a real cron job fix a slow admin?

It removes cron overhead from visitor requests, which can help with sporadic slow page loads. It does not fix slowness from the Heartbeat API, plugins or a slow database; see the speed checklist.

What is the difference between WP-Cron and the Heartbeat API?

They are unrelated. WP-Cron runs scheduled background tasks. The Heartbeat API is a browser feature that sends periodic AJAX requests while you are in the dashboard or editor, for autosave and post locking. Heartbeat monitoring in the sense of this guide means checking that a job pings a URL on schedule.

Get notified when this guide updates

Plus occasional practical tips on uptime and monitoring, nothing else. Unsubscribe anytime.

यह पेज सामान्य जानकारी है, जिसे हमारी संपादकीय टीम ने लिखा है और दस्तावेज़ीकरण बदलने पर अपडेट किया जाता है। सर्वर और प्रदाता अलग-अलग होते हैं, इसलिए बदलाव पहले सुरक्षित तरीके से जाँचें। देखें: संपादकीय नीति और अस्वीकरण.