搜索全站
报错排查 11 分钟阅读

WordPress 定时任务 wp-cron 原理:失效排查与改用系统 cron

定时发布的文章没发出来、备份任务不跑、索引不更新——多半是 wp-cron 没工作。本文讲清 wp-cron 的伪定时机制、四类失效原因、改用系统 crontab 的完整配置,以及用 WP-CLI 手动排查任务的命令。

U
UFO_PF 2017-03-20 · 阅读 11 分钟
Wo 报错排查
阅读提示
本文约 4,133 字,预计阅读 11 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:说好早上 8 点发布,10 点了还没发

定时发布是 WordPress 最常用的功能之一,也是最容易”静默失效”的:你在后台设了明早 8 点发布,第二天发现文章还躺在草稿里,后台顶部写着”错过计划”。

同样受影响的有:定时备份插件、邮件队列、Relevanssi 索引更新、WooCommerce 订单清理、插件更新检查。

这些全部依赖 WordPress 的定时任务系统 WP-Cron。它不是一个真正的定时器,理解这点,所有问题都有解。

原理分析:WP-Cron 是”伪定时”

Linux 的 crontab 是系统级守护进程,到点就执行。WP-Cron 完全不同:它靠页面访问触发。

每次有人访问站点,WordPress 加载时会检查 wp_options 里的 cron 数组,看有没有到期的任务,有就执行:

访客访问页面 → 触发 wp-cron.php?doing_wp_cron → 检查 cron 数组 → 执行到期任务 → 继续渲染页面

三个直接推论:

  1. 没人访问就不执行。日访问量个位数的站,夜间 12 小时无人访问,早 8 点的任务要等到第一个访客进来才触发——这就是”错过计划”的根源。
  2. 高流量站会拖慢页面。每次访问都要检查任务,且 wp-cron 会向自己发起一个 HTTP 请求。
  3. 被页面缓存影响。整页缓存拦截了请求,检查逻辑根本没跑到。

任务数据存在 wp_options 的 cron 选项里:

wp option get cron --format=json | head -60
wp cron event list

保姆级实操

第一步:确认 wp-cron 是否在跑

wp cron event list --fields=hook,next_run,next_run_gmt --format=table

对比 next_run_gmt 和当前时间,如果大量任务的下次执行时间早已过去却还没跑,说明 wp-cron 没工作。

第二步:四类失效原因

原因 1:DISABLE_WP_CRON 被设为 true

很多性能优化教程建议加:

define( 'DISABLE_WP_CRON', true );

这个常量关掉的是页面触发机制。加了它却没配系统 crontab,所有定时任务全部停摆。这是”定时发布失效”的第一大原因。

grep -n "DISABLE_WP_CRON" wp-config.php

原因 2:站点无人访问(低流量站)

原因 3:缓存/安全插件拦截 /wp-cron.php

curl -I "https://example.com/wp-cron.php?doing_wp_cron"

应返回 200 或 302,返回 403 就是被挡了。

原因 4:服务器禁止 PHP 自请求

有 HTTP Basic Auth、或 hosts 解析异常时,wp_remote_post 请求自身域名会失败。

第三步:改用系统 crontab(生产环境推荐)

一举解决”没人访问不执行”和”高流量拖慢页面”两个问题。

3.1 先关闭页面触发

// wp-config.php,写在 /* That's all */ 之前
define( 'DISABLE_WP_CRON', true );

3.2 加系统 crontab

crontab -e

每 5 分钟执行一次:

*/5 * * * * /usr/bin/php /var/www/example.com/wp-cron.php > /dev/null 2>&1

或者用 WP-CLI(更稳,能正确处理多站点上下文):

*/5 * * * * cd /var/www/example.com && /usr/local/bin/wp cron event run --due-now --quiet > /dev/null 2>&1

没 CLI 权限的虚拟主机用 wget:

*/5 * * * * /usr/bin/wget -q -O - "https://example.com/wp-cron.php?doing_wp_cron" > /dev/null 2>&1

3.3 确认 PHP 路径

which php && php -v

有的环境是 /usr/bin/php8.2,写错路径 crontab 会静默失败。

3.4 验证

grep CRON /var/log/syslog | tail -20
wp cron event run --due-now

第四步:手动执行任务(救急)

wp cron event run --due-now
wp cron event run wp_version_check
wp cron event run wp_scheduled_delete
wp cron event run wp_update_plugins
wp cron event schedule wp_version_check --time=now

定时任务挂掉导致的”错过计划”文章,执行 wp cron event run --due-now 后会自动补发。

第五步:添加自己的定时任务

add_action( 'init', function() {
    if ( ! wp_next_scheduled( 'mmf_hourly_task' ) ) {
        wp_schedule_event( time(), 'hourly', 'mmf_hourly_task' );
    }
});

add_action( 'mmf_hourly_task', function() {
    error_log( 'hourly task ran at ' . current_time( 'mysql' ) );
});

// 自定义间隔:每 15 分钟
add_filter( 'cron_schedules', function( $schedules ) {
    $schedules['every_15_minutes'] = array(
        'interval' => 900,
        'display'  => '每 15 分钟',
    );
    return $schedules;
});

停用插件时必须清理任务,否则残留 hook 会一直报错:

register_deactivation_hook( __FILE__, function() {
    $timestamp = wp_next_scheduled( 'mmf_hourly_task' );
    if ( $timestamp ) {
        wp_unschedule_event( $timestamp, 'mmf_hourly_task' );
    }
});

第六步:多站点

*/5 * * * * cd /var/www/example.com && for URL in $(wp site list --field=url); do wp cron event run --due-now --url="$URL" --quiet; done > /dev/null 2>&1

常见踩坑与失败案例

  1. 案例一:某站照着优化教程加了 DISABLE_WP_CRON,却没配 crontab,三个月后发现定时备份一次都没跑过,而站点正好在这期间被挂马,无备份可恢复。
  2. 案例二:crontab 里 PHP 路径写成 /usr/bin/php,但服务器上有多个 PHP 版本,CLI 默认是 5.6,任务执行时语法错误。用 php -v 确认路径。
  3. 任务重复堆积:插件在 init 里注册任务但没判断 wp_next_scheduled(),每次页面加载都新增一个,cron 选项膨胀到几 MB,后台变慢。清理:wp cron event delete mmf_hourly_task。
  4. wp cron event run 报 “This does not seem to be a WordPress installation”:没进站点根目录。crontab 里务必写 cd /path && wp ...。
  5. 案例三:站点加了 HTTP Basic Auth,wp-cron.php 的自请求被挡,定时任务全挂。解决:放行 /wp-cron.php,或改用 CLI 方式跑。
  6. 时区问题:wp_schedule_event 的时间戳用 UTC,后台显示本地时间。调试用 wp cron event list --fields=hook,next_run_gmt。
  7. 对象缓存影响:cron 选项被 Redis 缓存后,某些情况下任务执行完没写回。用 wp cache flush 排查。

总结

WP-Cron 是靠页面访问触发的伪定时器,没有访问就不执行——这是”定时发布错过”的根本原因。生产环境标准做法是:wp-config.php 里设 DISABLE_WP_CRON 关闭页面触发,再用系统 crontab 每 5 分钟跑 wp cron event run --due-now,既准时又不拖慢页面。排查用 wp cron event list 看下一条执行时间是否过期;救急用 wp cron event run --due-now 补跑。写插件注册定时任务时,务必先判断 wp_next_scheduled(),停用时 wp_unschedule_event() 清理。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。