# WP-Cron chạy chậm? Thay bằng cron của server trong 10 phút

> WP-Cron chạy chậm, bài hẹn giờ bị lỡ, email WooCommerce gửi trễ? Hướng dẫn tắt wp-cron và thay bằng cron hoặc systemd timer của server, kèm lệnh mẫu.

Source: https://zopanel.net/vi/blog/wp-cron-server-cron  
Updated: 2026-10-21

## Ý chính

- WP-Cron chỉ chạy khi có người truy cập, nên website ít khách bị lỡ lịch còn website đông khách tốn thêm request.
- Cách sửa chuẩn là đặt DISABLE_WP_CRON thành true và cho server gọi wp cron event run --due-now theo lịch.
- Chạy cron dưới quyền user của website, không phải root, và dùng flock để tránh chạy chồng.
- ZoPanel có tuỳ chọn để server chạy tác vụ định kỳ mỗi 5 phút bằng systemd timer, bật bằng một công tắc.

WP-Cron chạy chậm vì nó không phải cron thật: WordPress chỉ kiểm tra tác vụ đến hạn **khi có người truy cập trang**, rồi tự gửi một request tới `wp-cron.php`. Website ít khách thì bài hẹn giờ bị "Missed schedule" và email WooCommerce gửi trễ; website đông khách thì mỗi lượt kiểm tra lại tốn thêm một request PHP. Cách sửa chuẩn là đặt `DISABLE_WP_CRON` thành `true` trong `wp-config.php` và để cron (hoặc systemd timer) của server chạy `wp cron event run --due-now` theo lịch cố định.

Bài viết giải thích WP-Cron hoạt động thế nào, cách nhận biết nó đang gây chuyện, ba cách thay bằng cron của server (WP-CLI, gọi HTTP, systemd timer) kèm lệnh mẫu cho Ubuntu và Debian, những lỗi hay gặp, và cách bật bằng một công tắc trong ZoPanel.

## WP-Cron hoạt động như thế nào?

WordPress cần chạy việc theo lịch: đăng bài hẹn giờ, kiểm tra cập nhật, dọn dữ liệu tạm, gửi email đơn hàng, đồng bộ của plugin sao lưu hay SEO. Vì WordPress phải chạy được trên cả shared hosting không cho cấu hình cron, nó tự làm một "cron giả":

1. Ở mỗi lượt tải trang, WordPress xem danh sách sự kiện đã lên lịch (lưu trong bảng `wp_options`).
2. Nếu có sự kiện đến hạn, nó gửi một request ngầm (loopback) tới `https://tên-miền-của-bạn/wp-cron.php`.
3. Request đó chạy các sự kiện đến hạn trong một tiến trình PHP riêng.

[Tài liệu dành cho nhà phát triển của WordPress](https://developer.wordpress.org/plugins/cron/hooking-wp-cron-into-the-system-task-scheduler/) nói rõ: nếu không tắt, WordPress sẽ tiếp tục chạy WP-Cron ở mỗi lượt tải trang, kể cả khi bạn đã có cron của hệ thống. Cách này tiện cho người mới, nhưng gắn lịch chạy vào lưu lượng truy cập, và đó là gốc của mọi vấn đề.

## Vì sao WP-Cron chạy chậm hoặc không chạy?

**Website ít khách.** Không có lượt truy cập thì không có gì kích hoạt cron. Bài hẹn giờ 8:00 có thể tới 10:30 mới đăng, khi người đầu tiên ghé vào. WordPress đánh dấu những bài như vậy là "Missed schedule".

**Page cache chặn mất lượt kích hoạt.** Đây là nghịch lý ít ai để ý: trang được phục vụ từ page cache của nginx hay CDN không chạy PHP, nên WordPress không có cơ hội kiểm tra lịch. Website càng được cache tốt, WP-Cron càng ít được kích hoạt.

**Request loopback thất bại.** Server phải gọi được chính nó qua HTTPS. Nhiều thứ làm hỏng bước này: tên miền trỏ qua Cloudflare hoặc chưa trỏ về server, firewall chặn request từ chính IP của server, thư mục bị bảo vệ bằng mật khẩu, plugin bảo mật chặn `wp-cron.php`, hay chứng chỉ SSL lỗi. Công cụ **Tình trạng trang web** (Site Health) của WordPress có bài kiểm tra "loopback request" chính là để bắt lỗi này.

**Tốn tài nguyên trên website đông khách.** Mỗi lần có sự kiện đến hạn, một request PHP nữa được sinh ra cạnh request của khách. Khi tên miền phân giải chậm, mỗi lần gọi có thể giữ một PHP worker từ 10 tới 20 giây. Trên server chạy hàng trăm website, những worker bị giữ đó cộng lại thành áp lực thật lên RAM và CPU.

**Tác vụ nặng chạy trong request web.** Plugin sao lưu, gửi newsletter hay đồng bộ kho hàng chạy qua `wp-cron.php` chịu giới hạn thời gian của PHP-FPM và web server. Tác vụ dài bị cắt giữa chừng, rồi lần sau chạy lại từ đầu.

## Làm sao biết WP-Cron đang gây vấn đề?

Các dấu hiệu thường gặp:

- Bài viết hẹn giờ hiện "Missed schedule".
- Email đơn hàng, email đặt lại mật khẩu của plugin thành viên gửi trễ hàng giờ.
- Trong WooCommerce, trang **Trạng thái → Scheduled Actions** có nhiều tác vụ "Past-due".
- Site Health báo lỗi "loopback request" hoặc "scheduled event has failed".
- Log truy cập có rất nhiều dòng `POST /wp-cron.php` trên website đông khách.

Nếu có SSH, WP-CLI cho bạn câu trả lời nhanh nhất. Chạy trong thư mục của website, dưới quyền user sở hữu website:

```bash
wp cron test               # thử cơ chế spawn của WP-Cron
wp cron event list         # liệt kê sự kiện và thời điểm chạy tiếp theo
```

Một sự kiện có "next run" nằm trong quá khứ từ lâu là dấu hiệu WP-Cron không được kích hoạt đều.

## Cách thay WP-Cron bằng cron của server

### Bước 1: Tắt WP-Cron theo lượt truy cập

Mở `wp-config.php`, thêm dòng sau **phía trên** dòng `/* That's all, stop editing! */`:

```php
define( 'DISABLE_WP_CRON', true );
```

Hằng số này chỉ tắt việc kích hoạt theo lượt truy cập. Sự kiện vẫn được lên lịch như cũ, chỉ chờ ai đó chạy chúng. Vì vậy **đừng dừng lại ở bước này**: thiếu bước 2, website sẽ không bao giờ chạy tác vụ định kỳ nữa.

### Bước 2: Cho server chạy tác vụ đến hạn

Có ba cách, xếp theo mức chúng tôi khuyên dùng.

**Cách A: WP-CLI (khuyên dùng).** Không qua HTTP, không chịu timeout của web server, không phụ thuộc DNS hay SSL. Mở crontab của **user sở hữu website** (không phải root):

```bash
sudo crontab -u webuser -e
```

Thêm dòng:

```bash
*/5 * * * * cd /home/webuser/public_html && flock -n /tmp/wpcron-webuser.lock /usr/local/bin/wp cron event run --due-now --quiet
```

Giải thích từng phần:

- `*/5 * * * *`: chạy mỗi 5 phút.
- `cd ...`: vào thư mục chứa WordPress (hoặc dùng `--path=`).
- `flock -n`: nếu lần chạy trước chưa xong, lần này bỏ qua thay vì chạy chồng.
- `wp cron event run --due-now`: chạy mọi sự kiện đến hạn. Theo [tài liệu WP-CLI](https://developer.wordpress.org/cli/commands/cron/event/run/), lệnh này tôn trọng khoá `doing_cron` của WordPress để tránh chạy chồng.

Đường dẫn `wp` tuỳ cách bạn cài; kiểm tra bằng `which wp`. Với multisite, thêm `--network` để chạy cho mọi site con.

**Cách B: Gọi wp-cron.php qua HTTP.** Dùng khi không có WP-CLI:

```bash
*/5 * * * * curl -fsS -o /dev/null --max-time 120 "https://example.com/wp-cron.php?doing_wp_cron"
```

Cách này vẫn phụ thuộc DNS, SSL và giới hạn thời gian của PHP-FPM, nhưng ít ra lịch chạy không còn gắn với lượt khách. Đừng gọi qua `http://` nếu website ép chuyển hướng sang HTTPS, vì `curl` không theo redirect khi thiếu `-L`.

**Cách C: systemd timer.** Trên Ubuntu 24.04 và Debian 12, timer cho log rõ ràng hơn (`journalctl`), không chạy chồng (một service chỉ chạy một bản), và có `RandomizedDelaySec` để rải tải khi có nhiều website. Tạo `/etc/systemd/system/wpcron-example.service`:

```ini
[Unit]
Description=WordPress cron for example.com

[Service]
Type=oneshot
User=webuser
WorkingDirectory=/home/webuser/public_html
ExecStart=/usr/local/bin/wp cron event run --due-now --quiet
Nice=10
IOSchedulingClass=idle
```

và `/etc/systemd/system/wpcron-example.timer`:

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

[Timer]
OnCalendar=*:0/5
RandomizedDelaySec=60
Persistent=true

[Install]
WantedBy=timers.target
```

Bật lên:

```bash
sudo systemctl daemon-reload
sudo systemctl enable --now wpcron-example.timer
systemctl list-timers | grep wpcron
```

### Bước 3: Kiểm tra

```bash
sudo -u webuser wp --path=/home/webuser/public_html cron event list
```

Sau vài chu kỳ, cột "next run" phải luôn ở tương lai gần. Hẹn giờ một bài viết thử 10 phút sau và xem nó có đăng đúng giờ không.

## Nên chạy mỗi 1 phút, 5 phút hay 15 phút?

| Chu kỳ | Hợp với | Lưu ý |
|---|---|---|
| 1 phút | Cửa hàng đông đơn, hàng đợi email lớn | Tốn tài nguyên nhất; nhân với số website trên server |
| 5 phút | Đa số website, kể cả WooCommerce vừa và nhỏ | Cân bằng tốt; bài hẹn giờ trễ tối đa 5 phút |
| 15 phút | Blog, website giới thiệu | Ví dụ trong tài liệu WordPress dùng mốc này |

Lưu ý với WooCommerce: hàng đợi Action Scheduler của nó tự lên lịch chạy mỗi phút thông qua WP-Cron. Với cron 5 phút, tác vụ trong hàng đợi có thể chờ tối đa 5 phút, đủ tốt cho hầu hết cửa hàng. Cửa hàng rất đông có thể thêm một dòng cron riêng chạy `wp action-scheduler run` mỗi phút; [tài liệu Action Scheduler](https://actionscheduler.org/wp-cli/) khuyên dùng WP-CLI cho hàng đợi lớn.

Trên server có nhiều website, đừng để tất cả chạy đúng phút 0, 5, 10. Rải lịch (bằng `RandomizedDelaySec` hoặc lệch phút trong crontab) để tránh một đợt tăng tải đồng loạt.

## Những lỗi hay gặp

- **Chạy cron bằng root.** WP-CLI từ chối chạy bằng root nếu không có `--allow-root`, và có lý do: file cache, file log do tác vụ tạo ra sẽ thuộc về root, website không ghi được nữa. Plugin độc hại cũng sẽ chạy với quyền root. Luôn chạy dưới user của website.
- **Đặt DISABLE_WP_CRON mà quên tạo cron.** Website im lặng ngừng mọi tác vụ định kỳ. Tuần sau mới phát hiện vì không có bản sao lưu nào.
- **Đặt DISABLE_WP_CRON sau dòng "stop editing".** Khi đó `wp-settings.php` đã được nạp, hằng số không còn tác dụng.
- **Không có flock hay khoá.** Tác vụ nặng (sao lưu) chạy quá 5 phút, lần sau chồng lên, server quá tải.
- **Dùng ALTERNATE_WP_CRON như giải pháp.** Hằng số này chuyển hướng khách để chạy cron, chỉ dành cho trường hợp loopback hỏng mà không có cách khác. Nó không giải quyết chuyện phụ thuộc lượt truy cập.

## Thay WP-Cron bằng một công tắc trong ZoPanel

Trong ZoPanel, mở website, vào tab **WordPress**, tìm thẻ **Vận hành** và bật **Server chạy tác vụ định kỳ (5 phút/lần, không theo lượt khách)**. Theo [tài liệu WordPress Toolkit](/vi/docs/wordpress), ZoPanel sẽ:

1. đặt `DISABLE_WP_CRON` thành `true` trong `wp-config.php`;
2. tạo một systemd timer chạy `wp cron event run --due-now` mỗi 5 phút, **dưới quyền tài khoản hosting**, trong giới hạn tài nguyên của tài khoản đó và với mức ưu tiên CPU, ổ đĩa thấp. Các website được rải đều trong khoảng 5 phút, nên server chạy hàng trăm website không bị dồn tải vào cùng một thời điểm.

Website WordPress mới cài qua ZoPanel được chuyển sang chế độ này sẵn. Khi tắt công tắc, timer bị gỡ, và `DISABLE_WP_CRON` chỉ bị xoá nếu chính ZoPanel đã thêm nó; website tự tắt wp-cron từ trước vẫn giữ nguyên thiết lập của mình.

Điều này cũng là lý do page cache của ZoPanel không làm lỡ lịch: trang được phục vụ từ cache không cần kích hoạt WP-Cron nữa, vì server lo việc đó. Trong [bài đo số website WordPress trên VPS 4 GB](/vi/blog/how-many-wordpress-sites-4gb-vps), để server chạy tác vụ định kỳ là một trong những lời khuyên chúng tôi rút ra khi xếp hàng trăm website lên một server. Xem thêm [hiệu năng PHP](/vi/docs/php-performance) để hiểu cách PHP của website ít khách được cho "ngủ" mà tác vụ định kỳ vẫn chạy.

## Kết luận

WP-Cron là giải pháp hợp lý cho shared hosting đời cũ, nhưng trên VPS bạn có cron thật, và nên dùng nó. Hai bước là đủ: tắt WP-Cron theo lượt truy cập, rồi cho server chạy `wp cron event run --due-now` theo lịch, dưới quyền user của website. Bài hẹn giờ đăng đúng giờ, email gửi đúng lúc, và mỗi lượt xem trang nhẹ đi một chút.

Nếu muốn làm việc này cho hàng loạt website mà không phải tạo từng crontab, gói Free của ZoPanel có đủ WordPress Toolkit cho 10 website. Xem [bảng giá](/vi/pricing) khi cần nhiều hơn.

## Câu hỏi thường gặp

### Tắt WP-Cron có ảnh hưởng tới plugin không?

Không, nếu bạn thay bằng cron của server. Plugin vẫn lên lịch sự kiện như cũ; chỉ có cách kích hoạt thay đổi. Nếu tắt mà không tạo cron thay thế thì mọi tác vụ định kỳ của plugin sẽ ngừng.

### Vì sao bài viết hẹn giờ bị "Missed schedule"?

Vì không có lượt truy cập nào chạy PHP vào đúng thời điểm đó, thường do website ít khách hoặc trang được phục vụ từ cache. Request loopback tới `wp-cron.php` thất bại cũng gây ra lỗi này. Cron của server chạy mỗi 5 phút giải quyết cả hai trường hợp.

### Nên dùng wget/curl hay WP-CLI cho cron?

WP-CLI tốt hơn khi có thể: nó chạy trực tiếp bằng PHP CLI, không qua web server, nên không phụ thuộc DNS, SSL hay timeout của PHP-FPM. Gọi `wp-cron.php` bằng curl phù hợp khi không cài được WP-CLI.

### Shared hosting không có SSH thì làm sao?

Nhiều panel hosting có trang tạo cron job trong giao diện; dùng lệnh curl hoặc wget gọi `wp-cron.php` như ở cách B. Nếu nhà cung cấp dùng ZoPanel, chỉ cần bật công tắc **Server chạy tác vụ định kỳ** trong tab WordPress.

### Chạy cron mỗi phút có làm chậm server không?

Với một website thì không đáng kể, vì khi không có sự kiện đến hạn lệnh kết thúc rất nhanh. Trên server có hàng trăm website, mỗi lần chạy vẫn phải nạp WordPress, nên 5 phút là mức cân bằng tốt hơn, và nên rải lịch giữa các website.
