SSD Nodes Learn 🎉 VPS $4.99/月起
指南 Matt Connor作者: Matt Connor · 更新于 2026-08-07

WordPress 如何禁用 WP-Cron 并改用系统 Cron

WP-Cron 依赖页面访问,低流量站点会延迟任务,繁忙站点还会增加 PHP 开销。本文用 WP-CLI 配置系统 cron,并验证计划事件是否执行。

WP-Cron 是 WordPress 内置的任务调度器,只会在有人请求页面时运行。WordPress 不会自行唤醒。在每次未从缓存提供响应的请求中,WordPress 都会读取计划任务列表;如果某个任务已到执行时间,就会向自身的 /wp-cron.php 发起第二个 HTTP 请求来执行任务。将该任务交给系统 cron 后,无论这一分钟内网站有一千名访客还是没有访客,任务都会按固定计划可靠地运行。

真正执行任务的只有两行配置:wp-config.php 中的一个常量,以及一条 crontab 条目。本指南的其他内容,都是这两行配置无法说明的部分:任务必须以哪个用户身份运行,如何确认计划事件确实已执行,以及配置无任何站点提示时失败的三种方式。

示例使用 /srv/www/example.com 作为 WordPress 目录,使用 www-data 作为 Web 服务器用户。请在所有位置替换为您自己的路径和用户。

繁忙网站中,哪个访问者触发了 cron,成本是多少

每个未缓存的请求都要执行一次检查。WordPress 加载 cron 选项,比较时间戳;如果某项到期,就调用 spawn_cron(),向 /wp-cron.php 发送一个非阻塞回环请求。访问者无需等待结果,但 PHP worker 需要处理该请求。在运行 PHP-FPM 和 pm.max_children = 5 的小型 VPS 上,一个缓慢的计划任务会持续占用五分之一的 PHP 容量,直到任务完成。它最可能在最繁忙的一分钟内被触发,因为此时页面加载次数最多。

WordPress 会限制重复执行。它会获取一个有效期为 WP_CRON_LOCK_TIMEOUT 的锁,默认是 60 秒,因此同时访问的多个请求不会分别启动一次执行。该锁只能限制重复执行,不能将任务移出请求路径。

在判断这是否重要之前,先在自己的服务器上统计它的触发频率。每个回环请求都会出现在 Web 服务器访问日志中:

sudo grep -c 'wp-cron.php' /var/log/nginx/access.log

Apache 则会将其写入 /var/log/apache2/access.log。每天数千次的调用会产生实际成本。应在自己的服务器上测量这个数值,而不是直接采用文章中的数据;这与在其他变更前后 对 VPS 进行基准测试 的做法相同。

缓存会改变情况。如果页面缓存将大多数请求作为静态 HTML 提供,PHP 就不会处理这些请求,因此也不会执行 cron 检查。缓存率很高的繁忙网站,其行为会开始接近下面所述的安静网站。

流量触发的 cron 为什么会在低流量网站上失效

没有访客,就不会运行 cron。每天只有少量访问的网站,其计划任务每天也只会运行几次,而且运行时间取决于访客随机访问的时刻。

症状通常都一样。计划在 09:00 发布的文章会一直留在文章列表中,并标记为 Missed schedule,直到有人加载页面。备份插件会错过夜间备份。更新检查会延迟,因此安全版本已经发布,仪表盘却仍显示没有可用更新。订单邮件、续订通知和到期警告也会延迟发送。

这些问题都不会记录错误。在 WordPress 看来,任务并没有延迟,因为它根本没有启动。

第 1 步:在 wp-config.php 中关闭访客触发器

打开 /srv/www/example.com/wp-config.php,并添加以下常量:

define( 'DISABLE_WP_CRON', true );

将其放在内容为 /* That's all, stop editing! Happy publishing. */ 的行上方,因为该注释下方的行需要 wp-settings.php,而 wp-settings.php 是 WordPress 将 cron 检查挂接到 init 的位置。在该 require 语句之后定义常量就太晚了,无法产生任何效果;此时文件看起来配置正确,但触发器仍在运行。

确认该行位于预期位置:

grep -n "DISABLE_WP_CRON\|stop editing" /srv/www/example.com/wp-config.php

该常量不会停止事件调度。插件仍会像之前一样向队列中添加任务。它只会阻止页面加载处理该队列,因此在完成第 3 步之前,队列将永远不会运行。

它也不会阻止对 /wp-cron.php 的直接请求。任何人仍可请求该 URL。通常这没有问题,因为该文件只会运行到期的任务。在 Web 服务器配置中阻止该 URL 是可选的。但如果阻止,本文末尾附近的 curl 回退机制也会停止工作。

步骤 2:安装 WP-CLI

WP-CLI 是 WordPress 的官方命令行工具。它需要 PHP 命令行二进制文件。该文件与 Web 服务器使用的 PHP 模块属于不同的软件包。

php -v
sudo apt install -y php-cli

从 phar 构建版本安装 WP-CLI。官方安装指南推荐使用此方式:

cd /tmp
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
wp --info

php wp-cli.phar --info 会输出 PHP 二进制文件路径、PHP 版本和 WP-CLI 版本。如果这 3 项都能输出,说明 phar 可以正常工作。截至 2026 年 8 月,安装指南要求的最低 PHP 版本为 7.2.24,而 Ubuntu 24.04 附带 PHP 8.3,因此当前服务器的版本已明显高于该要求。之后可使用 sudo wp cli update 更新。

以网站用户身份运行 WP-CLI。不要以 root 身份运行:

sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com core version

以 root 身份运行时,WP-CLI 会拒绝启动:

Error: YIKES! It looks like you're running this as root.

它会建议使用 --allow-root。这里不要使用该选项。原因见下方的第一个故障模式。

还要注意,sudo -u www-data -i 无法正常工作,因为该账户的登录 shell 是 /usr/sbin/nologin,因此会得到 This account is currently not available.。将命令直接传递给 sudo -u 会绕过登录 shell,因此可以正常运行。

现在确认 WordPress 本身可以读取步骤 1 中定义的常量:

sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com eval 'var_dump( DISABLE_WP_CRON );'

该命令会输出 bool(true)。如果出现关于未定义常量的致命错误,说明没有执行到 define() 所在的行。通常是因为该行被放在了 require 语句之后。

第 3 步:以正确的用户添加 cron 条目

正确的用户是拥有 PHP 写入文件的用户。检查两端:

stat -c '%U %G' /srv/www/example.com/wp-content/uploads
grep -E '^(user|group) = ' /etc/php/8.3/fpm/pool.d/www.conf

在默认 Ubuntu 安装中,两者都会显示 www-data。如果您为网站配置了独立的 PHP-FPM 池,并为该池指定了专用用户(在 Ubuntu 24.04 上的 LAMP 堆栈中进行按站点配置时通常就是这种情况),请在下面的所有操作中使用该用户。

创建该用户可写入的日志目录:

sudo install -d -o www-data -g www-data -m 750 /var/log/wp-cron

编辑该用户的 crontab:

sudo crontab -u www-data -e

添加一行:

*/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-example.lock /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now >> /var/log/wp-cron/example.log 2>&1

逐项说明。*/5 每 5 分钟运行一次。flock -n 获取锁文件;如果上一次运行仍持有该锁,则立即退出。/usr/local/bin/wp 是绝对路径,cron 需要使用绝对路径。--path 允许命令从任意工作目录运行。--due-now 只运行已到执行时间的事件,而不是队列中的所有事件。重定向会将标准输出和错误输出写入同一个文件,您可以查看该文件。

实际使用时,不能省略这个重定向。Cron 会将任务输出通过邮件发送给任务用户,而大多数 VPS 镜像都未安装邮件传输代理。随后 cron 会记录 (CRON) info (No MTA installed, discarding output),并丢弃这些输出。写入文件可以保留故障证据。

检查文件是否已保存:

sudo crontab -u www-data -l

如果有多个网站,请每个网站使用一行,并错开执行分钟数,避免所有任务同时启动:

*/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-one.lock /usr/local/bin/wp --path=/srv/www/one.example.com cron event run --due-now >> /var/log/wp-cron/one.log 2>&1
2-59/5 * * * * /usr/bin/flock -n /run/lock/wp-cron-two.lock /usr/local/bin/wp --path=/srv/www/two.example.com cron event run --due-now >> /var/log/wp-cron/two.log 2>&1

日志会无限增长,除非对其进行轮换。写入 /etc/logrotate.d/wp-cron

/var/log/wp-cron/*.log {
    weekly
    rotate 4
    missingok
    notifempty
    compress
    create 640 www-data www-data
}

检查语法,但不要执行任何操作:sudo logrotate --debug /etc/logrotate.d/wp-cron

第 4 步:确认计划任务确实执行

成功保存 crontab 条目并不能证明任何事情。应从成本最低的检查开始,逐步定位到能够最终确认问题的检查。

首先,cron 是否启动了该命令?cron 会将日志写入其自身单元对应的 journal:

journalctl -u cron.service --since "15 min ago" | grep wp

健康的日志条目如下。示例已去掉开头的时间戳和主机名:

CRON[24913]: (www-data) CMD (/usr/bin/flock -n /run/lock/wp-cron-example.lock /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now >> /var/log/wp-cron/example.log 2>&1)

该行表示 cron 以 www-data 身份启动了命令。它不能说明命令是否执行成功。

其次,WordPress 是否执行了任务?读取日志文件:

sudo tail -n 20 /var/log/wp-cron/example.log

WP-CLI 会为每个事件输出一行,最后输出总数:

Executed the cron event 'wp_version_check' in 0.418s.
Success: Executed a total of 2 cron events.

错误也会写入同一个文件,这正是 2>&1 的作用。大多数运行时没有到期事件,因此写入内容很少。应在确认确实有待处理任务的一次运行后读取该文件。

第三,进行端到端验证。安排一个标记事件,然后观察它消失:

sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event schedule wp_cli_cron_check now
sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event list --fields=hook,next_run_relative --format=csv | grep wp_cli_cron_check

等待一个时间间隔,然后再次运行列表命令。该 hook 已消失,因为一次性事件执行后会从队列中删除。没有插件为该 hook 名称注册回调,因此运行它不会对站点执行其他操作。如果两个时间间隔后该 hook 仍在列表中,说明队列没有运行。前两项检查可以帮助判断问题出在 cron 还是 WP-CLI。

不要为此使用 wp cron test。该命令用于检查访客触发的任务生成是否正常;当 DISABLE_WP_CRON 为 true 时,它会报错。在配置正确的服务器上,该错误是预期输出,不表示故障。

systemd 定时器方案

如果服务器上的其他计划任务已经通过 systemd 服务和定时器运行,也应将 WordPress 放在 systemd 中。这样每次运行都会记录在 systemctl list-timers 中,输出也会写入 journal,而不必写入需要轮换的文件。

编写 /etc/systemd/system/wp-cron-example.service

[Unit]
Description=Run due WordPress cron events for example.com

[Service]
Type=oneshot
User=www-data
Group=www-data
ExecStart=/usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now

然后编写 /etc/systemd/system/wp-cron-example.timer

[Unit]
Description=Run WordPress cron for example.com every 5 minutes

[Timer]
OnCalendar=*:0/5
AccuracySec=30s
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now wp-cron-example.timer
systemctl list-timers wp-cron-example.timer
journalctl -u wp-cron-example.service -n 20

systemd 不会同时运行同一服务的两个实例,因此此方案不需要 flockPersistent=true 会在机器关机期间错过一次运行时,启动后补执行该任务,而 crontab 条目无法做到这一点。

请选择 crontab 或定时器之一。对同一站点同时运行两者,会导致队列被处理两次;电子邮件任务或订单任务重复运行后,客户会看到重复结果。

cron 任务不得以 root 身份运行

这是设置失败的三种方式中的第一种。将任务加入 root 的 crontab 后,WP-CLI 会在执行任何操作前停止:

Error: YIKES! It looks like you're running this as root.

队列不会运行。如果没有重定向输出,您也看不到这条消息。危险的修复方式是添加 --allow-root,因为这样插件在本次运行期间写入的所有文件都归 root 所有。下一次 Web 请求以 www-data 身份运行时,将无法写入这些目录,网站便会开始报告类似以下错误:

Unable to create directory wp-content/uploads/2026/08. Is its parent directory writable by the server?

修复所有权,然后移动任务:

sudo chown -R www-data:www-data /srv/www/example.com/wp-content

root 的 crontab 与 www-data 的 crontab 是两个独立的文件,因此从其中一个文件删除该行不会影响另一个文件。请检查两者:

sudo crontab -u root -l
sudo crontab -u www-data -l

cron 报告 wp: not found 的原因

这是第二个故障。cron 为用户任务提供的 PATH 很短,/usr/bin:/bin。WP-CLI 安装在 /usr/local/bin 中,而该路径不在列表内。任务启动后会在不到 1 秒内失败,日志中只会留下一行:

/bin/sh: 1: wp: not found

不要猜测 cron 的环境。可以直接查看。添加一行临时配置:

*/5 * * * * env > /tmp/cron-env.txt 2>&1

等待一个执行间隔后读取 /tmp/cron-env.txt,然后删除这一行。该文件中的 PATH= 值就是任务实际获得的环境变量值。

有两种修复方法。使用绝对路径 /usr/local/bin/wp,如第 3 步所示。或者在 crontab 顶部、所有任务行之前统一设置 PATH:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

同样的问题还存在于下一层。wp phar 文件以 #!/usr/bin/env php 开头,因此 shell 也必须能够找到 php。如果 PHP 位于 /usr/bin 之外,定制构建版本和控制面板构建版本中经常会出现这种情况,则会得到:

/usr/bin/env: 'php': No such file or directory

此时应显式调用解释器,例如 /usr/local/bin/php /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now

为什么 1 分钟间隔会重现原始问题

这是第三个故障。* * * * * 看起来比 5 分钟更安全,但在繁忙网站上,它会让问题回到起点。如果某次运行耗时超过设定间隔,下一次运行会在上一次仍未结束时启动。10 分钟后,系统中可能有 10 个 PHP 进程,每个进程都占用自己的内存,并保持自己的数据库连接。

直接检查是否发生堆积:

ps -eo etimes,user,args | grep '[c]ron event run'

etimes 表示进程运行时长,单位为秒。只有一行表示运行正常。如果有多行的运行时长远高于设定间隔,说明任务正在堆积。在小型 VPS 上,这最终可能导致 MySQL Too many connections 错误,也可能导致内核为回收内存而终止 PHP 进程。您可以使用 sudo dmesg -T | grep -i 'killed process' 确认这一点。

WP-CLI 会直接运行事件回调,而不是请求 wp-cron.php,因此 WordPress 用于防止重复启动的 60 秒锁在这里不适用。第 3 步条目中的 flock -n 现在负责防止重叠运行。跳过的任务会立即退出,且不会输出任何信息,这是设计如此。

将间隔设置为您实际依赖的最短计划周期,并先测量一次运行耗时:

time sudo -u www-data /usr/local/bin/wp --path=/srv/www/example.com cron event run --due-now

5 分钟是合理的默认值:计划在 09:00 发布的文章会在 09:05 前发布。对于没有时间关键任务的网站,15 分钟也可以。只有商店和确实需要快速处理的队列驱动插件才应使用 1 分钟间隔,而且应在确认每次运行只需几秒后再使用。

如果无法安装 WP-CLI

某些主机会阻止使用 shell 工具。向 wp-cron.php 发送普通 HTTP 请求也会执行同一队列,只是请求会经过完整的 Web 堆栈:

*/5 * * * * /usr/bin/curl -sS --max-time 120 "https://example.com/wp-cron.php?doing_wp_cron" > /dev/null

需要明确放弃的事项:

  • 执行时间受 Web 服务器和 PHP-FPM 请求超时限制,因此长时间任务可能在执行中途被终止。
  • 证书必须有效,否则 curl 会因 SSL certificate problem 停止运行。因此,请通过 在 nginx 上使用 Certbot 保持证书续期正常。
  • 页面缓存不能缓存 wp-cron.php,否则 cron 请求会获得缓存响应,任务不会执行。
  • 不会获得每个事件的输出,因此判断任务是否运行的唯一依据是它产生的实际效果。

-sS 在成功时让 curl 保持静默,但仍会输出错误信息。这正适合在 cron 任务中使用。

服务器的计划任务中还应包含哪些内容

一旦由系统 cron 负责 WordPress 队列,就应将服务器上的其他例行任务也放在同一处,便于统一查看。操作系统安全补丁应交给 无人值守升级,而不是手动维护的 cron 条目。WordPress 插件和主题更新则需要单独决定:将 wp plugin update --all 放入 crontab 后,可能在凌晨 3 点无人监控时直接导致线上站点故障,因此应有计划地执行,或先经过 staging 环境和备份步骤。

FAQ

禁用 WP-Cron 会停止发布定时文章吗?

不会,只要有其他程序运行队列即可。DISABLE_WP_CRON 只会阻止页面加载触发队列。事件仍会按原计划调度。设置为 09:00 发布的文章,会在 09:00 之后首次运行 cron 时发布,因此每五分钟运行一次时,最迟会在 09:05 发布。如果设置了该常量却从未添加 cron 条目,文章会一直留在列表中,并标记为 Missed schedule,直到有程序运行队列。

WordPress cron 任务应由哪个用户运行?

应由拥有 PHP 写入文件权限的用户运行。在默认 Ubuntu 安装中,该用户是 www-data。使用 stat -c '%U %G' /srv/www/example.com/wp-content/uploads 检查,并将结果与 PHP-FPM pool 配置中的 user = 行进行比较。以 root 运行任务会使 WP-CLI 因 YIKES 错误停止;使用 --allow-root 强制运行,则会在 wp-content 中留下由 root 拥有的文件,之后 Web 服务器无法写入这些文件。

system cron 应多久运行一次 WordPress cron?

每五分钟运行一次适合大多数站点。将间隔设置为不超过你实际依赖的最短调度周期,并确保该间隔明显长于单次运行所需的时间。你可以在 WP-CLI 命令前添加 time 来测量运行时长。在繁忙站点上,每分钟运行一次可能导致任务彼此叠加,除非使用 flock 防止并发运行。

禁用 WP-Cron 后,为什么 wp cron test 会失败?

因为该命令测试的是由访问者触发的任务生成机制;当 DISABLE_WP_CRON 设置为 true 时,它会报告错误。这正是此类服务器配置下的正确结果。请改为检查 system cron 路径:读取 /var/log/wp-cron/example.log,或使用 wp cron event schedule 调度一个标记事件,并在下一次运行后确认该事件已从 wp cron event list 中消失。

我需要 WP-CLI,还是使用 curl 请求 wp-cron.php 就够了?

curl 可以正常工作;无法安装 WP-CLI 时,使用 curl 是正确选择。它的速度较慢,因为它会通过 Web 服务器加载 WordPress,并受请求超时限制。WP-CLI 会在命令行 PHP 进程中运行事件,不受 Web 超时限制,并为每个事件输出一行及其耗时,因此日志可以准确显示运行了哪些事件以及各自耗时。

#wordpress#cron#wp-cli#性能#vps