本文约 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 数组 → 执行到期任务 → 继续渲染页面
三个直接推论:
- 没人访问就不执行。日访问量个位数的站,夜间 12 小时无人访问,早 8 点的任务要等到第一个访客进来才触发——这就是”错过计划”的根源。
- 高流量站会拖慢页面。每次访问都要检查任务,且 wp-cron 会向自己发起一个 HTTP 请求。
- 被页面缓存影响。整页缓存拦截了请求,检查逻辑根本没跑到。
任务数据存在 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
常见踩坑与失败案例
- 案例一:某站照着优化教程加了
DISABLE_WP_CRON,却没配 crontab,三个月后发现定时备份一次都没跑过,而站点正好在这期间被挂马,无备份可恢复。 - 案例二:crontab 里 PHP 路径写成
/usr/bin/php,但服务器上有多个 PHP 版本,CLI 默认是 5.6,任务执行时语法错误。用php -v确认路径。 - 任务重复堆积:插件在
init里注册任务但没判断wp_next_scheduled(),每次页面加载都新增一个,cron选项膨胀到几 MB,后台变慢。清理:wp cron event delete mmf_hourly_task。 wp cron event run报 “This does not seem to be a WordPress installation”:没进站点根目录。crontab 里务必写cd /path && wp ...。- 案例三:站点加了 HTTP Basic Auth,
wp-cron.php的自请求被挡,定时任务全挂。解决:放行/wp-cron.php,或改用 CLI 方式跑。 - 时区问题:
wp_schedule_event的时间戳用 UTC,后台显示本地时间。调试用wp cron event list --fields=hook,next_run_gmt。 - 对象缓存影响:
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() 清理。
