WordPress WP-Cron 卡住怎麼查?從 Missed Schedule 到系統 Cron 的排查筆記

WordPress 的排程問題很容易被誤判。文章設定了排程卻沒有發布、備份外掛沒有在凌晨執行、會員通知信延遲寄出,表面看起來像是某個外掛壞掉;但很多時候真正卡住的是 WP-Cron 沒有被觸發、系統 Cron 沒接好,或主機環境把請求擋掉。
我會先把這類問題當成網站維運事件,而不是立刻重裝外掛。因為排程牽涉文章發布、備份、快取、Email、同步與資料庫清理,動作太大反而可能讓原本可追查的線索消失。
先確認:是單一任務失敗,還是整個 WP-Cron 停住?
第一步不要急著改設定,而是先判斷範圍:
- 只有某篇排程文章 missed schedule?
- 只有某個備份或同步外掛沒跑?
- 所有定期任務都延遲?
- 前台流量很低,所以 WP-Cron 很少被觸發?
- 最近是否剛改過快取、WAF、Basic Auth、維護模式或主機排程?
如果只有單一外掛事件失敗,可能是外掛本身、API 額度或第三方服務問題;如果所有事件都延遲,就要優先查 WP-Cron 觸發方式。
用 WP-CLI 看事件清單
有 WP-CLI 的站台,我會先看 cron event:
wp cron event list --fields=hook,next_run,recurrence --format=table
接著找出已過期或特定外掛 hook:
wp cron event list --due-now
wp cron event list | grep -i backup
如果要測試是否能手動跑 due-now 事件,可以在維護窗口內執行:
wp cron event run --due-now
這一步的目標不是把所有問題一次跑完,而是確認 WordPress 本身能不能執行 cron callback。如果手動執行也報錯,就往外掛錯誤、PHP fatal error 或外部 API 查;如果手動可以,但平常不會自動跑,就往觸發機制查。
檢查 wp-config.php 是否停用了內建 WP-Cron
很多 production 站台會在 wp-config.php 加上:
define('DISABLE_WP_CRON', true);
這個設定本身不是錯。它的意思是:不要讓 WordPress 在前台訪問時自動觸發 wp-cron.php,改由系統 Cron 或主機排程定時呼叫。問題是,如果停用了內建 WP-Cron,卻沒有真的接上系統 Cron,排程就會安靜地堆積。
可以先查設定:
wp config get DISABLE_WP_CRON
如果結果是 true,下一步就要確認系統層是否有對應排程。
系統 Cron 要查執行者與路徑
常見的系統 Cron 寫法有兩種,一種用 HTTP 呼叫:
*/5 * * * * curl -s https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1
另一種用 PHP CLI:
*/5 * * * * cd /path/to/wordpress && php wp-cron.php >/dev/null 2>&1
我會檢查幾個細節:
- Cron 是哪個系統使用者在跑?權限是否能讀 WordPress 檔案?
php指到哪個版本?和網站使用的 PHP 版本是否差太多?- 網站是否有 Basic Auth、WAF、維護模式或防火牆擋住
wp-cron.php? - HTTP 呼叫是否回 200,而不是 403、404、500 或被 redirect 到登入頁?
- 主機是否有自己的排程介面,和手寫 crontab 重複或互相覆蓋?
HTTP 型 Cron 可以先測狀態碼:
curl -I https://example.com/wp-cron.php?doing_wp_cron
如果回的是 403 或 5xx,不要先怪 WordPress,先看主機存取規則、WAF 與錯誤日誌。
Missed Schedule 文章不要只重按更新
排程文章 missed schedule 時,最直覺的做法是重新按一次排程或直接發布;但如果這是內容站、品牌站或新聞型網站,我會先保留現場:
- 記錄文章 ID、原定發布時間與目前狀態。
- 查
wp_posts.post_status是否仍是future。 - 查 cron event 裡是否有
publish_future_post。 - 確認網站時區與伺服器時區是否有偏移。
可用 WP-CLI 快速看文章狀態:
wp post list --post_status=future --fields=ID,post_title,post_date,post_status
如果 missed schedule 是偶發,重跑 due-now 事件可能就能恢復;如果每天都發生,就要把 WP-Cron 觸發機制補穩,不然下次還會再卡。
快取與安全層也可能讓排程看起來失效
有些問題不是 WP-Cron 沒跑,而是它跑完後前台看起來沒更新。例如排程文章發布了,但首頁快取仍是舊版;或備份外掛完成了,但通知信被 SMTP、DNS、DMARC 或主機寄信限制擋住。
我會把排查分成幾層:
- WordPress 事件層:cron event 是否存在、是否 due、是否能手動跑。
- PHP / 外掛層:callback 是否報 fatal error、外掛是否有自己的 log。
- 主機排程層:crontab 或控制台排程是否真的執行。
- HTTP / 安全層:
wp-cron.php是否被擋、被 redirect、被快取。 - 前台呈現層:文章其實已發布,但頁面快取或 CDN 還沒刷新。
這和我在整理 WordPress 後台文章列表被 Object Cache 卡住 時的判斷很像:不要只看畫面,要把快取、資料庫、外掛與主機層拆開。
我會留下的維護紀錄
排程問題修完後,我會留一份簡短紀錄,方便下次比對:
事件:排程文章 missed schedule / 備份外掛未執行 / 其他
發現時間:
影響範圍:單一任務 / 多個 WP-Cron 事件 / 全站定期任務
DISABLE_WP_CRON:true / false
系統 Cron:有 / 無 / 主機控制台
測試指令:wp cron event list、wp cron event run --due-now、curl -I wp-cron.php
修復動作:
驗證結果:下一輪排程是否準時執行
回滾方式:恢復原 crontab / 還原 wp-config.php / 停用新調整
如果同一天還要更新外掛,我會搭配 WordPress 外掛更新 Smoke Test 清單,避免把排程問題和外掛更新風險混在一起。
小結:先穩定觸發,再處理單一外掛
WP-Cron 問題最怕的是一邊猜、一邊改、一邊清快取,最後不知道哪個動作讓網站恢復。比較安全的順序是:先確認事件是否存在,再確認觸發方式,接著看主機與安全層,最後才深入單一外掛或外部 API。
對內容站來說,排程不是小功能。它關係到文章發布、備份、清理、通知與自動化工作流;只要把 WP-Cron 的觸發與驗證流程整理清楚,很多看似隨機的 missed schedule 就能變成可追蹤、可修復的維運事件。