Securing WordPress Cron Jobs: Scheduled Task Security
Protect WordPress scheduled tasks from abuse, implement proper cron security, and monitor automated processes.
WordPress cron handles scheduled tasks like publishing posts, checking updates, and sending emails. Misconfigured cron can be abused for attacks or resource exhaustion.
Understanding WordPress Cron
How WP-Cron Works
- Not a real cron - triggered by page visits
- Runs on every page load if tasks are due
- Can cause performance issues on busy sites
- Can miss scheduled tasks on low-traffic sites
Security Implications
- Can be triggered externally (wp-cron.php is public)
- May execute malicious scheduled tasks
- Resource exhaustion from rapid triggering
- Data exposure from poorly written cron handlers
Disable Public WP-Cron
wp-config.php Setting
// Disable automatic cron triggering
define('DISABLE_WP_CRON', true);
Block Direct Access
# .htaccess - block external wp-cron access
<Files "wp-cron.php">
Order Deny,Allow
Deny from all
Allow from 127.0.0.1
Allow from ::1
</Files>
Use System Cron Instead
Linux Crontab
# Run WordPress cron every 5 minutes
*/5 * * * * wget -q -O /dev/null https://yoursite.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1
# Or using curl
*/5 * * * * curl -s https://yoursite.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1
# Or using WP-CLI (recommended)
*/5 * * * * cd /path/to/wordpress && wp cron event run --due-now >/dev/null 2>&1
Windows Task Scheduler
:: PowerShell script for scheduled task
Invoke-WebRequest -Uri "https://yoursite.com/wp-cron.php?doing_wp_cron" -UseBasicParsing
Secure Cron Handlers
Validate Cron Execution
// Ensure code only runs in cron context
add_action('wpfs_scheduled_task', 'wpfs_handle_scheduled_task');
function wpfs_handle_scheduled_task() {
// Verify this is a cron execution
if (!defined('DOING_CRON') || !DOING_CRON) {
return;
}
// Verify the action is legitimately scheduled
if (!wp_doing_cron()) {
return;
}
// Safe to proceed with task
wpfs_perform_task();
}
Time-Limited Execution
// Prevent cron from running too long
function wpfs_cron_with_timeout() {
$start_time = time();
$max_execution = 55; // Leave 5 seconds buffer
while ($items = get_pending_items()) {
if (time() - $start_time > $max_execution) {
// Reschedule remaining work
wp_schedule_single_event(time() + 60, 'wpfs_continue_task');
break;
}
process_item($items[0]);
}
}
Monitor Cron Activity
Log Cron Execution
// Log all cron events
add_action('wp_loaded', 'wpfs_log_cron_activity');
function wpfs_log_cron_activity() {
if (!defined('DOING_CRON') || !DOING_CRON) {
return;
}
$cron = _get_cron_array();
$running = array();
foreach ($cron as $timestamp => $hooks) {
if ($timestamp <= time()) {
$running = array_merge($running, array_keys($hooks));
}
}
if (!empty($running)) {
error_log('WP-Cron running: ' . implode(', ', $running));
}
}
Detect Suspicious Tasks
// Check for unknown scheduled events
function wpfs_audit_cron_events() {
$known_hooks = array(
'wp_version_check',
'wp_update_plugins',
'wp_update_themes',
'wp_scheduled_delete',
'wp_scheduled_auto_draft_delete'
// Add your known hooks
);
$cron = _get_cron_array();
$suspicious = array();
foreach ($cron as $timestamp => $hooks) {
foreach (array_keys($hooks) as $hook) {
if (!in_array($hook, $known_hooks)) {
$suspicious[] = $hook;
}
}
}
return array_unique($suspicious);
}
Rate Limit Cron Triggers
Prevent Rapid Triggering
// At the start of wp-cron.php or via plugin
$lock_file = sys_get_temp_dir() . '/wp_cron_lock';
$lock_time = 60; // Minimum seconds between runs
if (file_exists($lock_file)) {
$last_run = filemtime($lock_file);
if (time() - $last_run < $lock_time) {
exit('Cron rate limited');
}
}
touch($lock_file);
Clean Up Orphaned Events
// Remove cron events from deactivated plugins
function wpfs_cleanup_orphan_cron() {
$cron = _get_cron_array();
foreach ($cron as $timestamp => $hooks) {
foreach ($hooks as $hook => $events) {
if (!has_action($hook)) {
wp_clear_scheduled_hook($hook);
}
}
}
}
Conclusion
Secure WordPress cron by disabling public access, using system cron, validating handlers, and monitoring scheduled events. Regular audits catch malicious or orphaned tasks.
Written by Sarah Chen
WP Folder Shield Team