WP-Cron 不是常驻定时器。它挂在 WordPress 的 init 上,每次有请求进来时检查一次「有没有到期任务」,有就发一个非阻塞 HTTP 请求去执行 wp-cron.php。没有访客,就没有触发。系统 cron 由操作系统按时间表执行,不依赖网站流量。
判断标准:任务能不能容忍「等下一个访客」?能,继续用 WP-Cron;不能,换成系统 cron。
wp-includes/default-filters.php 里把 wp_cron() 挂到 init:
add_action( 'init', 'wp_cron' );wp_cron() 做三件事:检查是否已经在 cron 请求里、检查 DISABLE_WP_CRON、读取 cron 数组找最早的一个到期任务。找到后调用 spawn_cron()。
spawn_cron() 不执行任务,它只是向 wp-cron.php 发一个请求:
wp_remote_post( $cron_request['url'], array(
'timeout' => 0.01,
'blocking' => false,
'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
) );blocking => false 意味着发出去就不等结果。真正的任务在 wp-cron.php 这个独立的 PHP 请求里跑,它开头会:
@set_time_limit( 0 );
@ignore_user_abort( true );然后遍历到期事件逐个执行。
由此带来几个直接后果:
request_terminate_timeout、内存限制影响;set_time_limit(0) 也可能被 disable_functions 禁掉。doing_cron 这个 option,默认 60 秒(WP_CRON_LOCK_TIMEOUT)。任务跑超过 60 秒,下一轮请求可能再起一个进程,出现重叠。维度 | WP-Cron | 系统 cron |
|---|---|---|
触发者 | 访客请求 | 操作系统 |
精度 | 取决于流量 | 分钟级 |
是否依赖 loopback | 是 | 否 |
长任务 | 受 FPM 超时/内存限制 | CLI 无 web 超时 |
并发控制 |
| 可配合 |
失败可见性 | 默认静默 | 可写日志、可看退出码 |
被外部请求触发 | 可以 | 不涉及 |
按优先级:
1. 任务对时间敏感。 定时发布文章、会员到期降级、订阅续费、未支付订单自动取消、每日报表、邮件队列。晚几分钟可以,晚几小时就是事故。
2. 站点流量低,或者页面被整页缓存。 整页缓存命中时 PHP 不执行,init 不跑,WP-Cron 不触发。低流量站同理:没有访客就没有时钟。
3. 站点无法请求自己的域名。 常见于:DNS 还没切好、防火墙禁止出站、HTTPS 是自签证书、站点在容器内网、CDN 回源规则拦了 wp-cron.php。这时 spawn_cron() 的请求直接失败。
4. 有长任务。 备份、导入、图片批量处理、邮件队列。web 请求有超时,进程被 FPM 杀掉后任务停在半路,下次还可能重复。
5. wp-cron.php 被外部频繁请求。 任何人访问 /wp-cron.php 都会触发任务。锁能限制并发,但挡不住请求本身消耗 PHP-FPM 进程。
6. 多环境共用一个数据库。 staging 和 production 都会触发同一套 cron 事件,互相抢锁。需要按环境决定谁跑。
不适合换的情况也有:站点流量稳定、任务只有 wp_scheduled_delete 这类清理、没有时间敏感业务,继续用 WP-Cron 没问题,少一个系统依赖。
先备份 wp-config.php,在 staging 上做一遍。
wp cron event list --fields=hook,next_run_gmt,recurrence
wp cron test
wp config get DISABLE_WP_CRONwp cron event list 看有哪些事件、下次运行时间。如果 next_run_gmt 已经过去很久,说明触发链有问题。wp cron test 检查站点能否自己请求到 wp-cron.php。要在关闭 WP-Cron 之前跑。wp config get DISABLE_WP_CRON 确认常量是否已经被定义。未定义会报错,属于正常。再看一眼访问日志,确认现在是谁在触发:
grep 'wp-cron.php' /var/log/nginx/access.log | tail -n 20如果来源是大量不同的访客 IP,就是页面触发;如果只有固定几个 IP,可能已经有系统 cron 或监控在请求。
以 Ubuntu/Debian + Nginx + PHP-FPM 为例,其他环境替换路径。
在 wp-config.php 里加一行,放在 /* That's all, stop editing! */ 之前:
define( 'DISABLE_WP_CRON', true );这行只关闭「访客请求触发」,不关闭 wp-cron.php 本身。外部直接访问 /wp-cron.php 仍然会执行任务,所以后面还要限制 web 访问。
方式 A:WP-CLI(推荐)
/usr/local/bin/wp --path=/var/www/example.com/public cron event run --due-now--due-now 只跑到期的事件。WP-CLI 在 CLI 下加载 WordPress,不受 web 超时限制,出错有输出。
方式 B:直接跑 wp-cron.php
cd /var/www/example.com/public && /usr/bin/php wp-cron.php少一个 WP-CLI 依赖,但 CLI 下没有 $_SERVER['HTTP_HOST'] 等变量,个别插件可能报错。能用 A 就用 A。
先确认命令路径:
command -v wp
command -v php用站点所属用户,不要用 root:
sudo crontab -u www-data -e写入:
*/5 * * * * timeout 240 flock -n /tmp/wp-cron-example.lock /usr/local/bin/wp --path=/var/www/example.com/public cron event run --due-now --quiet >> /var/log/wp-cron-example.log 2>&1几个点:
flock -n:上一轮没跑完时直接跳过,不叠加。timeout 240:给这一轮一个上限,避免一个卡住的事件把锁一直占着。--quiet:正常执行不输出;报错仍会进日志。PATH 很短,wp、php 都写全路径。日志文件先建好并给权限:
sudo touch /var/log/wp-cron-example.log
sudo chown www-data:www-data /var/log/wp-cron-example.log不想留日志就把重定向改成 >> /dev/null 2>&1,但排查时会少一个依据。
对时间特别敏感的任务,可以把 */5 改成 *(每分钟)。由于有 flock,不会叠加;代价是每分钟多一个进程。
sudo -u www-data timeout 240 /usr/local/bin/wp --path=/var/www/example.com/public cron event run --due-now没有报错,再用列表确认下一次运行时间:
wp cron event list --fields=hook,next_run_gmt,recurrence等一个周期,看日志有没有新内容,以及 next_run_gmt 是否正常往后走。
如果用 CLI 跑,web 端不需要被执行。Nginx 里只允许本机:
location = /wp-cron.php {
allow 127.0.0.1;
allow ::1;
deny all;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php-fpm.sock;
}fastcgi_pass 的 socket 路径按 ls /run/php/ 的结果替换。
注意:如果站点在 CDN 后面,回源 IP 不是 127.0.0.1,上面的规则会把 CDN 也挡掉。这种情况要么放行回源 IP,要么不加这段,只依赖系统 cron 和 DISABLE_WP_CRON。
改完:
sudo nginx -t && sudo systemctl reload nginx每个站点单独跑,指定 --url:
*/5 * * * * timeout 240 flock -n /tmp/wp-cron-example.lock /usr/local/bin/wp --path=/var/www/example.com/public --url=https://example.com cron event run --due-now --quiet多站点网络里,每个子站有自己的 cron 事件,需要分别触发。
用 root 跑。 WP-CLI 会以 root 身份写 wp-content 下的文件,之后 web 用户改不动。用 crontab -u www-data 或 sudo -u www-data。
CLI 和 FPM 的 php.ini 不是一份。 memory_limit、disable_functions、扩展都可能不同。用 php -i | grep memory_limit 和 FPM 的配置对比。
--path 指错。 要指到 wp-config.php 所在目录,不是 wp-content。
时区。 cron 按服务器时区执行,WordPress 的定时事件按 UTC 存。跨时区团队先用 timedatectl 确认服务器时区,避免差几个小时。
锁卡住。 doing_cron 这个 option 如果残留,会挡住后续调度。先看:
wp option get doing_cron确认没有任务在跑,再删除:
wp option delete doing_cron不要在任务执行期间删。
插件自己的队列。 有些插件把队列挂在 WP-Cron 上,切换后要在插件设置里确认队列仍在跑。用 wp cron event list 能看到对应 hook。
CDN 缓存了 wp-cron.php**。** 如果 CDN 规则把 PHP 当静态资源缓存,要排除这个路径。
改了 DISABLE_WP_CRON 但没加系统 cron。 结果就是所有定时任务停摆。先加系统 cron,再关页面触发,顺序别反。
wp cron test 通过(关闭前)wp-config.php 里 DISABLE_WP_CRON 为 truewp / php 使用绝对路径flock,长任务有 timeoutcron event run --due-now 无报错next_run_gmt 正常推进wp-cron.php 已限制,或确认不需要限制如果站点是交给别人部署的,把这一项写进交付清单:确认 DISABLE_WP_CRON 和系统 cron 都已配置,并留下日志位置。我们的代部署服务会把定时任务一起配好,交付时附一页说明:Setup。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。