How WordPress Cron Really Works (And Why It Matters for Plugin Developers)
WordPress Cron (WP-Cron) is often misunderstood, even by experienced developers. Many assume it works like a real system cron daemon, but in reality, it behaves very differently. This article explains exactly how WP-Cron is triggered, how wp-cron.php is called, and why this knowledge is essential when building performance-sensitive plugins like FDP.
WP-Cron Is Not a Real Cron Daemon
Unlike a real cron job managed by the operating system, WP-Cron is a pseudo-cron system. It does not run on a fixed schedule by itself. Instead, it relies on normal page loads to decide when scheduled tasks should be executed.
In simple terms:
- Someone visits a page on your site
- WordPress checks if there are scheduled tasks due
- If needed, WordPress triggers
wp-cron.php
The Frontend Request That Starts Everything
The process starts when a visitor (or a bot) requests any frontend page of your site:
GET /some-page/
This request goes through index.php, and WordPress begins its normal bootstrap process. At this point, plugins are loaded, hooks are registered, and WordPress prepares to render the page.
How WordPress Decides to Run Cron
During the bootstrap phase, WordPress calls the function wp_cron(), which is hooked to the init action. This function checks whether cron should be spawned.
Internally, WordPress evaluates several conditions:
- Are there scheduled events that are due?
- Is cron already running?
- Has cron been triggered too recently?
- Is
DISABLE_WP_CRONenabled?
If any of these checks fail, nothing happens and the page continues loading normally.
The Asynchronous Call to wp-cron.php
If WordPress determines that cron should run, it does not execute cron tasks in the same request. Instead, it fires a separate, asynchronous HTTP request to:
wp-cron.php?doing_wp_cron=TIMESTAMP
This request is made using wp_remote_post() with a very small timeout and blocking set to false. The visitor’s page load is not slowed down.
This logic lives in:
wp-includes/cron.php → spawn_cron()
wp-cron.php Runs in a Separate PHP Process
The request to wp-cron.php is a completely separate HTTP request. WordPress is bootstrapped again from scratch, but with some important differences:
DOING_CRONis defined astrue- The active theme is not loaded
- Admin context is not active
- Plugins are loaded as usual
This is why cron callbacks must not rely on admin-only files or functions unless they explicitly include them.
Execution of Scheduled Hooks
Once WordPress is loaded, it loops through all scheduled cron events that are due and fires their hooks using do_action().
If a hook has no callback attached, nothing happens and no error is thrown. If a callback produces a fatal error, that specific cron execution stops.
WordPress also uses a lock (stored as the doing_cron transient) to prevent multiple cron processes from running at the same time.
What Happens When WP-Cron Is Disabled
If DISABLE_WP_CRON is set to true, WordPress will never spawn cron automatically. In this case, cron must be triggered manually using a real system cron, for example:
php wp-cron.php
or via an HTTP request:
https://example.com/wp-cron.php
Well-written plugins do not need to handle this difference explicitly. They simply register scheduled hooks.
Why This Matters for Plugin Developers
Understanding the WP-Cron flow is essential when building advanced plugins such as FDP:
- Cron runs during frontend traffic
- Cron is not admin context
- Cron may never run on low-traffic sites
- Heavy tasks must be optimized and throttled
For tasks like sending URLs to Google PageSpeed Insights, proper batching, locking, and defensive loading of classes are mandatory to avoid performance issues.
Conclusion
WP-Cron is simple in concept but subtle in execution. Once you understand that it is triggered by frontend requests and runs in a separate PHP process, many common cron-related bugs suddenly make sense.
It is important to clarify a common misconception: WP-Cron does not slow down the page that triggers it. Cron jobs are executed in a separate, asynchronous request to wp-cron.php, so the visitor who loads the page does not wait for cron tasks to complete.
The real downside is different. Every cron execution still consumes server resources: PHP workers, CPU time, memory, database connections, and sometimes external API quotas. On busy sites, frequent or heavy cron jobs can run in parallel and overload the server, even if individual page loads appear fast.
On low-traffic sites, the opposite problem can occur: cron jobs may not run at all unless traffic arrives. In both cases, long-running tasks, external HTTP requests, or unthrottled loops increase the risk of overlapping executions, timeouts, and partial failures.
For plugin developers, this means cron jobs must be treated as production-critical code paths: lightweight, throttled, resumable, and defensively coded. When used correctly, WP-Cron is a powerful scheduling system. When misused, it becomes an invisible source of instability.
If you build plugins that rely on scheduled tasks, mastering WP-Cron is not optional. It’s a core skill.
