WP Webhooks / Blog / WordPress internals
Article · WordPress internals

WordPress External Cron: Services, Setup, and Free Options

Fix unreliable WP-Cron with a free external cron service. Covers cron-job.org, EasyCron, UptimeRobot setup, DISABLE_WP_CRON config, and the zero-config Webhook Actions option.

8 min 2026-06-05
#wordpress#cron#reliability

TL;DR

  • WP-Cron fires on page load, not on a real schedule — low-traffic sites can miss events for hours
  • Fix it by pinging /wp-cron.php?doing_wp_cron every minute from an external service
  • Free options: cron-job.org, UptimeRobot, EasyCron
  • Always add define('DISABLE_WP_CRON', true) to wp-config.php after setting up an external cron
  • Webhook Actions Pro includes a managed zero-config external cron — details in the content below

/ Why WP-Cron fails

Why does WP-Cron fail on low-traffic sites?

WordPress ships with a built-in scheduling system called WP-Cron. Unlike a real cron daemon, WP-Cron does not run on a timer. It runs on page load: every time a visitor (or bot) requests a WordPress page, the site checks whether any scheduled jobs are due and runs them inline. This works acceptably on high-traffic sites because there is always a page load happening. On low-traffic sites — staging environments, small business sites, early-stage products — large gaps between page loads mean scheduled jobs pile up unprocessed.

The practical consequences: a scheduled email digest fires at 3 AM only if someone visits the site between 2:59 AM and 3:01 AM. A webhook queue runner that should fire every minute runs only as often as traffic arrives. A plugin that expires licenses daily may not run for 18 hours on a quiet Sunday.

External cron replaces the page-load trigger with a real scheduled HTTP ping — a service you control, running on its own schedule, regardless of site traffic.

WP-Cron (default)
  visitor request → wp-cron spawned → runs due jobs
  ↑ only fires when traffic exists

External cron service (correct)
  cron-job.org / UptimeRobot / EasyCron
        │
        │ every 1 min (scheduled HTTP GET)
        ▼
  GET /wp-cron.php?doing_wp_cron
        │
        ▼
  WordPress runs all due jobs
  (queues, emails, deliveries, expirations)
        │
        └─ returns 200 (jobs ran) or 200 empty (nothing due)
FIG 01 — WP-Cron (page-load) vs external cron service

/ The cron URL

What URL do you ping for WordPress external cron?

The standard URL for triggering all WordPress scheduled jobs is:

https://yourdomain.com/wp-cron.php?doing_wp_cron

Replace yourdomain.com with your actual domain. The ?doing_wp_cron query parameter tells WordPress this is an intentional external trigger rather than a side-effect of a page load. The endpoint always returns HTTP 200 — even when no jobs were due — so status monitoring alone is not a reliable way to confirm cron ran. See the verification section below for a better approach.

If your site uses HTTPS (it should), always use the HTTPS URL. Most external cron services follow redirects, but some do not — use the canonical HTTPS URL directly to avoid a redirect on every ping.

/ cron-job.org setup

How do you set up WordPress external cron with cron-job.org?

cron-job.org is a free HTTP cron service with no rate limiting on 1-minute intervals, email failure notifications, and an execution log for each job. No payment is required for standard use.

  1. Create an account at cron-job.org — free, email verification required.
  2. Add a new cron job. In the dashboard click Create cronjob. Set the URL to https://yourdomain.com/wp-cron.php?doing_wp_cron and the request method to GET.
  3. Set the schedule. Choose Every 1 minute under the schedule picker. For low-traffic sites, every minute is the right starting point — drop to every 5 minutes only if you have no sub-5-minute scheduled tasks.
  4. Enable failure notifications. Under Notifications, enable email alerts on failure. This is your early warning when the WordPress site is down or wp-cron.php is returning non-200 responses.
  5. Add DISABLE_WP_CRON to wp-config.php. See the next section — this step is required to prevent page loads from triggering cron in parallel.

/ Other free services

What other free external cron services work with WordPress?

UptimeRobot (free tier: up to 50 monitors, 5-minute minimum interval) is primarily an uptime monitor but doubles as a cron trigger — add a monitor of type HTTP(s) pointing at your wp-cron.php URL and set the check interval to 5 minutes. The free plan does not support 1-minute intervals, but for most sites a 5-minute cron window is acceptable. You also get uptime history as a bonus.

EasyCron offers a free tier with one cron job running every 20 minutes — suitable only for low-frequency tasks. Paid plans unlock 1-minute intervals. For single-site WordPress with budget constraints, EasyCron's free tier is usable but not recommended for sites with webhook queues that need frequent flushing.

Healthchecks.io works differently: it is a dead man's switch, not an active pinger. You configure a check with an expected ping interval, then your cron job pings the Healthchecks URL to signal it ran. Healthchecks alerts you when it stops receiving pings. For WordPress external cron, you would use a system cron entry or another service as the actual trigger, and Healthchecks for monitoring. It is free for up to 20 checks.

/ DISABLE_WP_CRON

How do you configure DISABLE_WP_CRON correctly?

After setting up an external cron service, add this constant to wp-config.php before the /* That's all, stop editing! */ line:

wp-config.php — disable page-load cron trigger

// Add before "That's all, stop editing!"
define( 'DISABLE_WP_CRON', true );

Add this only after your external cron service is active and confirmed running. If you disable WP-Cron before the external service is live, no scheduled jobs will run at all — including transient expiration, update checks, and your own webhook queues.

Without DISABLE_WP_CRON, every page load attempts to spawn a cron worker via a non-blocking loopback request to wp-cron.php. This means your external service and page-load traffic are both trying to claim and run the same due jobs. In most cases WordPress's lock mechanism prevents double-execution, but the wasted HTTP round-trips add overhead to every page load. Disabling the page-load trigger makes cron timing fully deterministic.

/ REST endpoint vs wp-cron.php

When should you use a plugin REST endpoint instead of wp-cron.php?

Pinging wp-cron.php runs all due jobs across every plugin on the site. For most WordPress installations this is fine. But there are cases where you want to run only one plugin's queue, with tighter control over batch size, or at a shorter interval than 60 seconds.

Webhook Actions Pro exposes a dedicated cron endpoint: /wp-json/fswa/v1/cron/process?token=<your-token>. Pinging this URL processes only the Webhook Actions delivery queue — not email campaigns, not license checks, not anything from other plugins. You can configure the batch size (1–100 items per ping) and run it as frequently as every 20 seconds from an external service. The WP-Cron endpoint has a minimum interval of 60 seconds because WordPress's own lock mechanism throttles faster pings. See the External Cron feature docs for configuration details.

ApproachIntervalScopeSetup
wp-cron.php (page-load, default)On traffic onlyAll plugins
External service → wp-cron.phpMin ~1 minAll plugins — simple, free
External service → plugin REST endpointMin 20sOne plugin only — fine-grained, Pro

/ Verification

How do you verify your external cron is actually running?

HTTP 200 from wp-cron.php only means WordPress responded — it does not confirm any jobs ran. Two reliable verification methods:

Check a plugin's queue log. Webhook Actions shows per-delivery timestamps in its Logs screen. After enabling external cron, trigger a test delivery and confirm the timestamp matches the expected interval. If the delivery log shows deliveries clustered at irregular intervals, page-load cron is still running alongside your external trigger — check whether DISABLE_WP_CRON is set correctly.

Use WP-CLI from the server. If you have server access, wp cron event run --due-now manually fires all due events and prints which hooks executed. Run it after your external cron fires and confirm the expected hooks appear in the output.

/ Zero-config option

What is the zero-config alternative to managing external cron yourself?

If you are running Webhook Actions Pro, you do not need to configure cron-job.org, UptimeRobot, or any external service manually. The External Cron feature provisions a managed Uptime Kuma heartbeat monitor automatically when you activate your Pro license. It handles DISABLE_WP_CRON configuration, sets the ping interval (down to 20 seconds in plugin-endpoint mode), and shows a live heartbeat chart with monitor status directly in the Webhook Actions admin screen.

For teams already managing external cron manually — via a server crontab, hosting panel, or one of the services above — the external cron approach in this article is completely valid and costs nothing. The Webhook Actions managed option is for teams who want reliability without any server or service configuration overhead. See the WP-Cron developer reference for the full wp_schedule_event and WP-CLI cron command reference if you need finer-grained control.

External cron is not a plugin feature — it is a fundamental requirement for any WordPress site that relies on scheduled jobs running at predictable times.
/Footnotes
³ cron-job.org FAQ — free tier limits and interval options.
UptimeRobot — HTTP monitor service with free tier supporting 5-minute check intervals.
FAQ

Common questions always ask.

Don't see yours? Open an issue on GitHub or check the full reference in the API docs.

What is the best free external cron service for WordPress? +
cron-job.org is the most widely used free option — it supports custom intervals as short as one minute, HTTP monitoring, and email alerts with no account payment required for basic use. UptimeRobot is a free alternative that doubles as a site uptime monitor. EasyCron offers a free tier for low-frequency jobs.
What URL should an external cron service ping for WordPress? +
The standard URL is https://yourdomain.com/wp-cron.php?doing_wp_cron — a GET request every one to five minutes. Before configuring the service, add define("DISABLE_WP_CRON", true) to wp-config.php so page loads no longer trigger cron in parallel.
What is DISABLE_WP_CRON and why should you enable it with external cron? +
DISABLE_WP_CRON is a wp-config.php constant that prevents WordPress from triggering its cron system on every page load. Without it, page loads compete with your external cron pings, causing duplicate cron runs. When using an external service, DISABLE_WP_CRON makes cron timing fully predictable.
What is the difference between pinging wp-cron.php and a plugin REST endpoint? +
Pinging wp-cron.php runs all pending WordPress cron jobs from every plugin on the site. A plugin REST endpoint like the Webhook Actions /wp-json/fswa/v1/cron/process endpoint runs only that plugin's delivery queue, with fine-grained control over batch size and shorter minimum intervals (20s vs 60s for wp-cron.php).
Is the External Cron feature in Webhook Actions available on the free plan? +
No. External Cron is a Pro feature in Webhook Actions. It auto-provisions a managed Uptime Kuma heartbeat monitor on Pro license activation, handles DISABLE_WP_CRON configuration automatically, and provides a live heartbeat chart and monitor status in wp-admin. Free users can configure any of the manual services described in this article.
Ready

Stop losing webhooks.
Start logging them.

$ wp plugin install flowsystems-webhook-actions --activate