کرون وردپرس کار نمی‌کند و این جمله‌ای است که وقتی از زبان صاحب سایت می‌شنوید، تقریباً همیشه یعنی یکی از سه چیز: بکاپ‌ها اجرا نمی‌شوند، ایمیل‌های زمان‌بندی‌شده نمی‌رسند، یا افزونه امنیتی چند روز است اسکن نکرده. این علائم ظاهراً متفاوت‌اند ولی ریشه‌شان معمولاً یکی است: مکانیزم WP-Cron که به بازدید کاربران وابسته است، در سکوت از کار افتاده. من در بازبینی سایت‌های واقعی، بیش از بیست الگوی متفاوت دیده‌ام که به این وضعیت می‌رسند و تجربه‌ام این است که عیب‌یابی سیستماتیک این لایه، یکی از فنی‌ترین بخش‌های کار با وردپرس است.

اگر با مکانیزم پایه WP-Cron آشنا نیستید، پیش از ادامه کرون وردپرس و زمان‌بندی خودکار کارها را بخوانید؛ این نوشته فرض می‌کند که با اصول کار، تفاوتش با cron سیستمی و ساختار آرایه cron در دیتابیس آشنایید. موضوع این مقاله، دقیقاً برعکس آن است: وقتی همه‌چیز نظری درست تنظیم شده ولی در عمل کار نمی‌کند، چه کار باید کرد.

نشانه‌های خرابی WP-Cron: چه چیزی را می‌بینید؟

قبل از اینکه به کد و دیتابیس بپردازیم، باید بدانید که چه نشانه‌هایی در واقعیت نماینده خرابی WP-Cron هستند. تجربه‌ام این است که توسعه‌دهندگان تازه‌کار، در تشخیص این نشانه‌ها اشتباه می‌کنند و منبع مشکل را در جای غلط جستجو می‌کنند. جدول زیر، شایع‌ترین نشانه‌ها و تفاوت‌شان را با مشکلات مشابه نشان می‌دهد.

نشانهمعنای محتملچه چیزی را نفی می‌کند
بکاپ با تاریخ نامنظم ذخیره می‌شودWP-Cron وابسته به ترافیکمشکل در افزونه بکاپ نیست
ایمیل‌های خبرنامه با تأخیر چند ساعته می‌رسندصف cron انبار شدهمشکل SMTP نیست
رویداد در WP-CLI با زمان گذشته نشان داده می‌شودرویداد اجرا نشده و در صف ماندهمشکل تنظیمات نیست
افزونه امنیتی روزها اسکن نکردهرویداد cron یتیم یا جا افتادهمشکل امنیتی نیست
صفحه بروزرسانی وردپرس به‌طور ناگهانی کند شدهانبار رویدادهای تکراریمشکل سرعت هاست نیست
فهرست رویدادها در پنل هاست خالی استcron سیستمی تنظیم نشدهمشکل PHP نیست

یکی از اشتباهات رایجی که در پروژه‌های واقعی زیاد دیده‌ام، انتساب مشکل به افزونه‌ای است که کار زمان‌بندی را انجام می‌دهد. مثلاً وقتی بکاپ اجرا نمی‌شود، اولین واکنش توسعه‌دهنده تازه‌کار این است که افزونه بکاپ را تغییر دهد یا مجدداً نصب کند. درحالی‌که خود افزونه سالم است و فقط مکانیزم WP-Cron در پشت صحنه، به‌دلیل ترافیک کم یا مشکلات دیگر، رویداد را اجرا نمی‌کند. این تفکیک بین «مشکل رویداد» و «مشکل افزونه»، اولین اصل عیب‌یابی است.

وقتی بکاپ اجرا نمی‌شود، اولین کسی که باید متهم شود افزونه بکاپ نیست؛ مکانیزم WP-Cron است. اکثر مشکلات به این لایه برمی‌گردند نه به کد افزونه‌ها.

چرا WP-Cron از کار می‌افتد: هفت علت ریشه‌ای

در بازبینی سایت‌های واقعی، هفت علت ریشه‌ای تکرارشونده دیده‌ام که باعث می‌شوند WP-Cron از کار بیفتد. هر کدام نشانه‌های خودش را دارد و هرکدام نیازمند رویکرد تشخیصی متفاوتی است. در این بخش، هر علت را جداگانه باز می‌کنم.

علت اول: ترافیک ناکافی

این شایع‌ترین علت است. WP-Cron توسط بازدید کاربران بیدار می‌شود، پس اگر سایتی در ساعت‌های مشخص ترافیک ندارد، رویدادهای آن ساعت به تأخیر می‌افتند یا اصلاً اجرا نمی‌شوند. یک سایت با کمتر از صد بازدید در روز، عملاً برای WP-Cron کم‌ترافیک محسوب می‌شود و در این شرایط، بکاپ‌های روزانه با تأخیر چند ساعته اجرا می‌شوند و بکاپ‌های هفتگی گاهی تا دو روز عقب می‌افتند.

در یکی از پروژه‌های واقعی، سایتی با حدود پنجاه بازدید در روز داشتیم که همه بکاپ‌هایش با تأخیر چهار تا پنج ساعته اجرا می‌شدند. تجربه ام این است که در این شرایط، مهاجرت به cron سیستمی، تنها راه‌حل پایدار است. راه‌حل‌های میانی مثل سرویس‌های پینگ خارجی هم کار می‌کنند ولی وابستگی به سرویس سوم را اضافه می‌کنند.

علت دوم: غیرفعال بودن WP-Cron بدون جایگزین

گاهی توسعه‌دهنده یا مدیر سایت، ثابت DISABLE_WP_CRON را در فایل wp-config.php فعال می‌کند ولی cron سیستمی را تنظیم نمی‌کند. این وضعیت، بدترین سناریو است چون همه رویدادها برای همیشه از دست می‌روند و در ظاهر هیچ خطایی هم در لاگ‌ها دیده نمی‌شود. تشخیص این وضعیت ساده است: اگر در wp-config.php این ثابت true باشد ولی در پنل cPanel هیچ cron سیستمی وجود نداشته باشد، این علت قطعی است.

علت سوم: مسدود بودن wp-cron.php

بعضی افزونه‌های امنیتی یا قوانین فایروال، درخواست‌های مستقیم به فایل wp-cron.php را به‌عنوان «حمله ممکن» مسدود می‌کنند. این اتفاق در پروژه‌هایی رخ داده که افزونه امنیتی، قوانین سختگیرانه‌ای روی درخواست‌های همگام HTTP اعمال کرده. برای تشخیص، یک درخواست مستقیم به آدرس سایت با پارامتر wp-cron.php?doing_wp_cron بفرستید و ببینید کد پاسخ ۲۰۰ می‌گیرید یا ۴۰۳. اگر ۴۰۳ دیدید، قوانین امنیتی سایت مانع اجرای cron می‌شوند.

علت چهارم: انبار رویدادهای تکراری

این علت، رایج‌ترین دام کدی است که من در بازبینی افزونه‌های تازه‌کار دیده‌ام. اگر در کد ثبت رویداد، شرط wp_next_scheduled نباشد، هر بار که افزونه لود شود، یک ردیف تکراری در آرایه cron اضافه می‌شود. بعد از یک ماه با ترافیک متوسط، آرایه cron می‌تواند هزاران ردیف داشته باشد و همین باعث می‌شود سریالایز و دیسریالایز آن، در هر بازدید چند صد میلی‌ثانیه صرف کند. اگر با ساختار افزونه و اهمیت این جزئیات آشنا نیستید، ساختار استاندارد یک افزونه حرفه‌ای وردپرس را از این نظر مطالعه کنید.

نشانه دیگر انبار رویدادهای تکراری، کندی محسوس پیشخوان وردپرس است. در یکی از پروژه‌ها، کاربر شاکی بود که صفحه مدیریت افزونه‌ها پنج ثانیه طول می‌کشد تا باز شود. بعد از بررسی، مشخص شد آرایه cron حدود هشت هزار ردیف تکراری دارد و همین، بار سنگینی روی دیتابیس است. علت اصلی، تابعی بود که بدون شرط wp_next_scheduled در هوک init رویداد ثبت می‌کرد. اگر با مکانیزم ثبت هوک‌ها آشنایی کمتری دارید، نحوه استفاده از add_action در وردپرس پیش‌نیاز این بحث است.

علت پنجم: رویدادهای یتیم از افزونه‌های حذف‌شده

وقتی افزونه‌ای غیرفعال و حذف می‌شود، اگر در کدش تابع register_deactivation_hook و پاک‌سازی رویدادها رعایت نشده باشد، رویدادهای cron آن افزونه در دیتابیس باقی می‌مانند. این رویدادها در هر بازدید سایت بررسی می‌شوند ولی چون تابع مربوطه دیگر وجود ندارد، هیچ کاری انجام نمی‌دهند. پیامد مستقیم این وضعیت، کندی تدریجی سایت است که در نهایت به مشکلات جدی‌تری تبدیل می‌شود. این دسته از مشکلات را در مدیریت داده‌های موقت در وردپرس به‌عنوان یکی از چهار منبع اصلی داده یتیم معرفی کرده‌ام.

علت ششم: تعارض با افزونه‌های بهینگی یا کش

بعضی افزونه‌های کش یا بهینه‌سازی سرعت، درخواست‌های cron را نیز کش می‌کنند یا به‌طور ناخواسته مسدود می‌کنند. این اتفاق در افزونه‌هایی رخ می‌دهد که همه درخواست‌های PHP را در یک صف قرار می‌دهند و درخواست‌های همگام cron از دستشان در می‌رود. نشانه این علت، این است که با غیرفعال کردن افزونه کش، رویدادها بلافاصله اجرا می‌شوند و با فعال کردن مجدد آن، دوباره مشکل برمی‌گردد. این نوع تعارض‌یابی، فرآیندی آزمایشی است و در تست و دیباگ پروژه‌های توسعه وردپرس روش سیستماتیک آن آمده است.

علت هفتم: محدودیت منابع هاست

در هاست‌های اشتراکی با منابع محدود، ممکن است درخواست cron در میانه اجرا کشته شود چون از محدودیت CPU یا حافظه عبور کرده. این وضعیت سخت‌ترین تشخیص را دارد چون در ظاهر، cron اجرا می‌شود ولی در نیمه راه متوقف می‌شود. نشانه‌اش این است که رویدادهای سبک اجرا می‌شوند ولی رویدادهای سنگین — مثل پاک‌سازی دیتابیس یا اسکن امنیتی — نیمه‌کاره می‌مانند. تحلیل دقیق این محدودیت‌ها در چگونه مصرف منابع هاست را کاهش دهیم آمده است.

هر علت خرابی WP-Cron، یک اثر انگشت منحصربه‌فرد دارد؛ عیب‌یابی حرفه‌ای یعنی پیدا کردن آن اثر انگشت نه آزمون‌وخطای تصادفی.

مرحله تشخیص: WP-CLI، دیتابیس و لاگ‌ها

حالا که علل ریشه‌ای را می‌شناسید، برویم سراغ ابزارهای تشخیص. تجربه‌ام این است که اگر عیب‌یابی به‌شکل سیستماتیک انجام شود، در اکثر موارد زیر بیست دقیقه می‌توان علت را قطعی کرد. سه ابزار اصلی در این مسیر WP-CLI، دسترسی مستقیم به دیتابیس و لاگ‌ها است.

WP-CLI: ابزار اول و مهم‌ترین

اگر روی سرور به SSH دسترسی دارید، WP-CLI ابزار اصلی شما است. سه دستور کلیدی که در همه عیب‌یابی‌ها استفاده می‌کنم:

wp cron event list
wp cron event list --fields=hook,next_run_relative,recurrence --format=table
wp cron event run myplugin_daily_task

دستور اول، فهرست کامل همه رویدادهای cron را نشان می‌دهد. دستور دوم با فیلدهای مشخص، تصویر دقیق‌تری از رویدادها می‌دهد و در واقع ابزار روزانه من است. دستور سوم، یک رویداد مشخص را به‌اجبار اجرا می‌کند. اگر دستور سوم به‌درستی کار کرد ولی رویداد در حالت عادی اجرا نمی‌شود، مشکل در ثبت رویداد یا در مکانیزم بیدار شدن WP-Cron است، نه در خود تابع.

نکته‌ای که در پروژه‌های واقعی به آن برخورده‌ام: در بعضی هاست‌های اشتراکی، WP-CLI در دسترس نیست و باید آن را دستی نصب کنید. اگر روی PHP ۷.۴ یا بالاتر هستید، نصب WP-CLI حدود پنج دقیقه وقت می‌گیرد و ارزش سرمایه‌گذاری دارد. اگر با محیط خط فرمان آشنا نیستید، سرور چیست و چگونه کار می‌کند پایه‌های فنی را روشن می‌کند.

بررسی مستقیم آرایه cron در دیتابیس

اگر به WP-CLI دسترسی ندارید، می‌توانید مستقیم از دیتابیس آرایه cron را بخوانید:

SELECT option_value
FROM wp_options
WHERE option_name = 'cron';

خروجی این کوئری، یک رشته سریالایز است که در نگاه اول غیرقابل خواندن به نظر می‌رسد. دو روش برای قابل‌خواندن کردنش وجود دارد: اول، از تابع PHP unserialize در یک اسکریپت موقت استفاده کنید؛ دوم، از ابزار آنلاین آنسریالایز PHP (با احتیاط امنیتی، چون داده سایت را در سرور شخص ثالث می‌فرستد). در پروژه‌های واقعی، روش اول را ترجیح می‌دهم چون داده از سرور خارج نمی‌شود.

یک نشانه کلیدی که در این آرایه به‌دنبالش هستم: تعداد ردیف‌ها. اگر تعداد ردیف‌های آرایه بیش از بیست باشد و سایت شما کمتر از پنج افزونه فعال داشته باشد، به‌احتمال زیاد رویدادهای تکراری یا یتیم دارید. این وضعیت در سایت‌هایی که چند سال بدون پاک‌سازی کار کرده‌اند، تقریباً همیشه وجود دارد. اینجاست که درک مکانیزم ذخیره‌سازی cron در دیتابیس، تفاوت بین یک عیب‌یابی دقیق و یک آزمون‌وخطای طولانی است.

لاگ‌ها: منبع چه چیزی که کد نمی‌گوید

سه نوع لاگ در این مرحله مفید هستند: لاگ PHP در wp-content/debug.log، لاگ خطاهای سرور در پنل هاست، و لاگ درخواست‌های HTTP در صورت وجود. اگر با حالت دیباگ وردپرس آشنا نیستید، ابتدا باید ثابت‌های WP_DEBUG و WP_DEBUG_LOG را در wp-config.php فعال کنید. بعد از فعال‌سازی، هر خطای PHP در حین اجرای cron در فایل لاگ ثبت می‌شود.

نکته‌ای که در پروژه‌های واقعی اهمیت دارد: در سایت‌های پرمصرف، فایل debug.log می‌تواند در چند روز به چند گیگابایت برسد. پس از عیب‌یابی، حتماً این فایل را پاک کنید و WP_DEBUG_LOG را غیرفعال کنید. فعال نگه داشتن این حالت در محیط production، به‌دلیل مصرف دیسک و ریسک امنیتی، توصیه نمی‌شود. بهترین رویکرد، تشخیص مشکل در محیط staging و اعمال راه‌حل در production است.

رفع رویدادهای تکراری: قاتل خاموش دیتابیس

رویدادهای تکراری، شایع‌ترین مشکل ساختاری WP-Cron هستند و همیشه از یک الگوی کد اشتباه ریشه می‌گیرند. عیب‌یابی و رفع این مشکل، در پروژه‌های واقعی معمولاً با بازگشت سرعت سایت به حالت طبیعی همراه است.

الگوی اشتباه و پیامدش

الگوی اشتباه که در نود درصد موارد دیده‌ام:

add_action( 'init', function () {
    wp_schedule_event( time(), 'daily', 'myplugin_task' );
} );

این کد در هر بازدید کاربر، یک رویداد جدید ثبت می‌کند. بعد از یک هفته با ترافیک متوسط، آرایه cron می‌تواند صدها ردیف تکراری داشته باشد و بعد از یک ماه به هزاران ردیف برسد. پیامد آن، کندی محسوس در بارگذاری هر صفحه سایت است چون در هر بازدید، آرایه بزرگ سریالایز و دیسریالایز می‌شود. اثر این مشکل روی سرعت دیتابیس، دقیقاً همان چیزی است که در تاثیر دیتابیس بر سرعت سایت چقدر است توضیح داده شده است.

الگوی درست و اصلاح سریع

الگوی درست، با شرط wp_next_scheduled همراه است:

add_action( 'init', function () {
    if ( ! wp_next_scheduled( 'myplugin_task' ) ) {
        wp_schedule_event( time(), 'daily', 'myplugin_task' );
    }
} );

این تغییر کوچک، تفاوت بین یک افزونه سالم و یک افزونه مضر است. اگر افزونه‌ای که این اشتباه را داشته با نسخه اصلاح‌شده جایگزین شود، رویدادهای تکراری موجود در دیتابیس به‌طور خودکار پاک نمی‌شوند و باید به‌طور دستی پاک شوند. برای پاک‌سازی، از WP-CLI استفاده می‌کنم:

wp cron event delete myplugin_task
wp cron event schedule myplugin_task now daily

دستور اول، همه رویدادهای با این نام را حذف می‌کند و دستور دوم یک رویداد تازه با بازه روزانه ثبت می‌کند. این الگو، در اکثر پروژه‌ها در کمتر از یک دقیقه کار را تمام می‌کند.

پاک‌سازی نسخه قدیمی افزونه

نکته‌ای که در پروژه‌های واقعی اهمیت دارد: اگر افزونه‌ای با این اشتباه، سال‌ها روی سایت بوده، ممکن است آرایه cron به‌قدر بزرگ شده باشد که حتی دستور WP-CLI هم کند شود. در این حالت، روش مستقیم از دیتابیس موثرتر است:

DELETE FROM wp_options
WHERE option_name = 'cron';

-- بلافاصله بعد از حذف، WP-Cron در اولین بازدید، رکورد جدید خالی می‌سازد

هشدار مهم: پیش از اجرای این کوئری، بکاپ دیتابیس ضروری است. حذف کل آرایه cron، همه رویدادها را پاک می‌کند و اگر بعد از آن، رویدادها دوباره ثبت نشوند، سایت شما برای همیشه بدون cron کار می‌کند. بعد از حذف، باید یک بازدید از سایت انجام دهید تا وردپرس، رکورد جدید cron را با مقادیر پیش‌فرض بسازد و رویدادهای اصلی خودش را ثبت کند. روش استاندارد بکاپ در چگونه از سایت وردپرسی بکاپ بگیریم آمده است.

یک خط شرط wp_next_scheduled، تفاوت بین یک افزونه سالم و یک بمب ساعتی است؛ در پروژه‌های واقعی، بیشتر مشکلات WP-Cron از نبود همین شرط کوچک ناشی می‌شود.

رویدادهای جامانده و یتیم: پاک‌سازی دقیق

رویدادهای یتیم، آن‌هایی هستند که از افزونه‌های حذف‌شده به‌جا مانده‌اند و هیچ کدی آن‌ها را مدیریت نمی‌کند. این رویدادها در هر بازدید سایت بررسی می‌شوند ولی چون تابع مربوطه وجود ندارد، بی‌اثر هستند. رفع این مشکل، دو مرحله دارد: شناسایی و پاک‌سازی.

شناسایی رویدادهای یتیم با WP-CLI

دستور wp cron event list فهرست همه رویدادها را نشان می‌دهد. برای تفکیک رویدادهای یتیم از فعال، معمولاً دو مسیر دارم. مسیر اول، تطبیق نام رویداد با پیشوند افزونه‌های فعال است. اگر نام رویدادی با پیشوند myplugin_ شروع شود ولی افزونه‌ای با این پیشوند نصب نباشد، رویداد یتیم است. مسیر دوم، بررسی زمان آخرین اجرای موفق است؛ رویدادهایی که زمان اجرای بعدی‌شان بسیار در گذشته است و بعد از چند اجرای موکول، هنوز اجرا نشده‌اند، کاندید بررسی هستند.

پاک‌سازی گام‌به‌گام

پاک‌سازی رویدادهای یتیم، مثل هر عملیات دیتابیس، باید با احتیاط انجام شود. الگویی که در پروژه‌ها استفاده می‌کنم، سه گام دارد. گام اول، فهرست گرفتن از همه رویدادهای مشکوک و ثبت آن در یک فایل متنی. گام دوم، پاک‌سازی دسته‌ای با دستور wp cron event delete. گام سوم، بررسی تأثیر پاک‌سازی روی سایت در طول چند روز.

wp cron event list --fields=hook --format=csv > suspicious-events.csv
# بررسی دستی فایل و شناسایی رویدادهای یتیم
wp cron event delete old_plugin_daily_task
wp cron event delete abandoned_theme_cleanup

این رویکرد، ریسک پاک‌سازی اشتباه را به حداقل می‌رساند چون در گام اول، همه اطلاعات برای بازگشت ذخیره می‌شود. اگر بعد از پاک‌سازی، رویداد مهمی حذف شده باشد، می‌توانید از فایل CSV، نام دقیق آن را استخراج و مجدداً ثبت کنید.

جلوگیری از شکل‌گیری رویدادهای یتیم

در پروژه‌های خودم، جلوگیری از شکل‌گیری رویدادهای یتیم را با یک قاعده ساده رعایت می‌کنم: هر افزونه‌ای که رویداد cron ثبت می‌کند، باید در تابع register_deactivation_hook، همه رویدادهایش را پاک کند. این قاعده، در استانداردهای رسمی وردپرس هم تأکید شده و اگر افزونه‌ای این را رعایت نکند، نشانه‌ای از سطح حرفه‌ای پایین سازنده است. به‌عنوان توسعه‌دهنده، اگر افزونه‌ای را با این مشکل پیدا کردید، یکی از گزینه‌ها این است که یک افزونه کمکی کوچک بنویسید که هنگام غیرفعال‌سازی افزونه اصلی، رویدادهای آن را پاک کند.

وقتی WP-Cron غیرفعال شده ولی جایگزین نیست

این سناریو، اگر نگوییم بدترین، یکی از دردناک‌ترین موارد است چون در ظاهر همه‌چیز سالم به نظر می‌رسد ولی در واقع هیچ رویدادی اجرا نمی‌شود. تجربه‌ام این است که این وضعیت معمولاً زمانی رخ می‌دهد که توسعه‌دهنده قبلی، مراحل مهاجرت به cron سیستمی را نیمه‌کاره رها کرده و ثابت DISABLE_WP_CRON را فعال کرده ولی cron سیستمی را راه‌اندازی نکرده است.

تشخیص قطعی

مرحله تشخیص سه گام ساده دارد. گام اول، بررسی فایل wp-config.php برای دیدن ثابت DISABLE_WP_CRON. گام دوم، بررسی cron سیستمی در پنل cPanel یا DirectAdmin. گام سوم، بررسی فایل‌های پیاده‌سازی cron سیستمی در صورت وجود. اگر گام اول true باشد و گام دوم خالی باشد، این علت قطعی است.

راه‌حل: راه‌اندازی cron سیستمی

راه‌حل، تکمیل مهاجرت به cron سیستمی است. سه خط cron که در پنل cPanel ثبت می‌کنم:

*/5 * * * * wget -q -O - https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1

پارامتر doing_wp_cron در انتهای URL ضروری است چون وردپرس از این پارامتر می‌فهمد که درخواست، یک درخواست cron است نه بازدید کاربر. بدون آن، مکانیزم WP-Cron به‌درستی فعال نمی‌شود. اگر با cPanel آشنایی کمتری دارید، بخش Cron Jobs در همان پنل، رابط گرافیکی ساده‌ای دارد که می‌توانید از آن استفاده کنید. تشخیص و راه‌حل این سناریو را در کرون وردپرس و زمان‌بندی خودکار کارها به‌عنوان بخشی از الگوهای پایدار آورده‌ام.

بازگشت اضطراری به WP-Cron

اگر به هر دلیلی نمی‌توانید cron سیستمی را راه‌اندازی کنید — مثلاً هاست شما اجازه نمی‌دهد — راه‌حل اضطراری این است که ثابت DISABLE_WP_CRON را کامنت کنید یا مقدارش را به false برگردانید و از سرویس‌های پینگ خارجی برای بیدار کردن WP-Cron استفاده کنید. این رویکرد، مسکن است نه درمان، ولی از بی‌کار افتادن کامل رویدادها بهتر است.

نکته کلیدی در بازگشت اضطراری این است که سایت را در این حالت طولانی رها نکنید. اگر بیش از چند هفته طول می‌کشد تا هاست مناسب پیدا کنید، دیتابیس شما به‌تدریج پر از رکوردهای cron موکول‌شده می‌شود و این خودش منبع مشکلات جدید است. راه‌حل درست، مهاجرت به هاستی است که امکان cron سیستمی را فراهم کند. برای انتخاب هاست مناسب، هاست چیست و چگونه انتخاب درستی داشته باشیم معیارهای واقعی را بررسی می‌کند.

سایت‌های کم‌ترافیک و معماهای اجرای معوق

سایت‌های کم‌ترافیک، یکی از دشوارترین سناریوها هستند چون مشکل در ظاهر نامرئی است. سایت به‌نظر سالم کار می‌کند و فقط برخی وظایف دوره‌ای، با تأخیر یا به‌طور نامنظم اجرا می‌شوند. این وضعیت، از نقطه‌نظر کاربر عادی قابل تشخیص نیست ولی از نظر فنی، به کاهش پایداری سایت در بلندمدت منجر می‌شود.

شبیه‌سازی برای تشخیص

برای تشخیص قطعی این وضعیت، سه روش دارم. روش اول، پایش دستی است: هر چند ساعت یک بار، WP-CLI را اجرا کنید و فهرست رویدادهای موکول‌شده را ببینید. اگر رویدادی با زمان اجرای گذشته در فهرست دیده شود، یعنی WP-Cron آن را به‌موقع اجرا نکرده است. روش دوم، لاگ‌نویسی از اجرای رویداد است. یک کد دیباگ موقت اضافه می‌کنم که در هر اجرا، timestamp ثبت می‌کند:

add_action( 'myplugin_daily_task', function () {
    error_log( 'Task executed at: ' . current_time( 'mysql' ) );
} );

روش سوم، استفاده از سرویس‌های مانیتورینگ خارجی است که در بازه‌های منظم، به سایت درخواست می‌فرستند و در همان بازه، رویدادها بیدار می‌شوند. این سرویس‌ها معمولاً آمار Uptime ارائه می‌دهند که در همین زمینه هم مفید است.

راه‌حل‌های عملی

سه راه‌حل برای سایت‌های کم‌ترافیک وجود دارد که هر کدام مزایا و معایب خودش را دارد. راه‌حل اول، سرویس پینگ خارجی است که در بازه‌های پنج‌دقیقه‌ای به سایت درخواست می‌فرستد. مزیتش این است که بدون تغییر در سایت کار می‌کند. عیبش وابستگی به سرویس خارجی است و اینکه برای سایت‌های حساس امنیتی، درخواست‌های خارجی اضافی ممکن است مشکل‌ساز شود.

راه‌حل دوم، مهاجرت به cron سیستمی است. این راه‌حل، بهترین گزینه است چون مستقل از ترافیک کار می‌کند و جزو استانداردهای حرفه‌ای مدیریت سایت‌های وردپرسی است. برای اکثر سایت‌های کم‌ترافیک، این راه‌حل مشکل را برای همیشه حل می‌کند. اگر با محدودیت‌های هاست اشتراکی مواجهید، ممکن است مجبور باشید به هاستی با قابلیت cron سیستمی مهاجرت کنید.

راه‌حل سوم، تغییر ماهیت رویدادها است. به‌جای اینکه رویداد در ساعت مشخصی اجرا شود، می‌توانید رویداد را به رویدادهای مبتنی بر رویداد (مثلاً هر بار انتشار نوشته، اجرای پاک‌سازی) تغییر دهید. این رویکرد در بعضی سناریوها کار می‌کند ولی نمی‌تواند جایگزین کامل رویدادهای زمان‌محور مثل بکاپ روزانه باشد.

تعارض افزونه‌ها با WP-Cron

یکی از دشوارترین دسته مشکلات WP-Cron، تعارض با افزونه‌های دیگر است. این تعارض‌ها معمولاً در پروژه‌هایی ظاهر می‌شوند که تعداد افزونه‌ها زیاد است و رویدادهای مختلف با هم در تداخل قرار می‌گیرند. تجربه‌ام این است که این نوع تعارض، در آمار واقعی حدود ده درصد موارد را تشکیل می‌دهد.

سه الگوی رایج تعارض

الگوی اول، تعارض با افزونه‌های کش است که درخواست‌های HTTP داخلی را کش می‌کنند. اگر افزونه کش، درخواست‌های cron را نیز کش کند، وردپرس تصور می‌کند cron اجرا شده ولی در واقع اجرا نشده. علامت این تعارض، این است که رویدادها در WP-CLI به‌عنوان اجرا شده ثبت می‌شوند ولی اثرشان در سایت دیده نمی‌شود.

الگوی دوم، تعارض با افزونه‌های امنیتی است که فایل wp-cron.php را در فهرست مسدودها قرار می‌دهند. بعضی افزونه‌های امنیتی سختگیرانه، همه درخواست‌های مستقیم به فایل‌های PHP را به‌عنوان حمله ممکن تلقی می‌کنند. اگر با ساختار افزونه‌های امنیتی آشنا نیستید، بهترین افزونه‌های امنیتی وردپرس برای محافظت از سایت را ببینید.

الگوی سوم، تعارض ناشی از پاک‌سازی تهاجمی است. بعضی افزونه‌های بهینه‌سازی دیتابیس، در پاک‌سازی خود، آرایه cron را نیز پاک می‌کنند. اگر افزونه بهینه‌سازی، رکورد cron را با گزینه‌های دیگر اشتباه بگیرد، ممکن است همه رویدادها پاک شوند. نشانه این تعارض این است که بعد از هر پاک‌سازی، همه رویدادها ناپدید می‌شوند.

روش تشخیص تعارض

روش سیستماتیک تشخیص تعارض، سه گام دارد. گام اول، غیرفعال کردن همه افزونه‌ها و فعال کردن WP-Cron. گام دوم، فعال کردن افزونه‌ها یکی‌یکی و بررسی رویدادها. گام سوم، باریک‌کردن به تعارض مشخص. این رویکرد استاندارد در چگونه افزونه مشکل‌ساز وردپرس را پیدا کنیم به‌طور مشابه برای مشکلات دیگر توضیح داده شده است.

تأیید نهایی: چگونه مطمئن شویم رفع شد؟

پس از رفع مشکل، مرحله تأیید به همان اندازه مرحله تشخیص اهمیت دارد. تجربه‌ام این است که در پروژه‌های واقعی، اگر تأیید درست انجام نشود، مشکل بعد از چند هفته به‌طور خاموش برمی‌گردد. برای تأیید قطعی، سه روش را به‌طور موازی استفاده می‌کنم.

روش اول: پایش سه روزه

بعد از رفع مشکل، سه روز پایش دقیق انجام می‌دهم. در هر روز، فهرست رویدادها را از WP-CLI می‌گیرم و زمان اجراهای واقعی را یادداشت می‌کنم. اگر در طول سه روز، هیچ رویدادی با تأخیر بیش از یک ساعت اجرا نشد، مشکل برای آن رویداد برطرف شده است. این پایش، ساده ولی موثر است.

روش دوم: لاگ‌نویسی موقت

در بعضی سناریوها، لاگ‌نویسی موقت مفید است. یک کد کوچک که در هر اجرای رویداد، timestamp را لاگ می‌کند. پس از سه تا هفت روز، فایل لاگ را بررسی می‌کنم و اگر تاریخ‌ها با فاصله‌های منظم ظاهر شده باشند، تأیید نهایی صورت گرفته است. بعد از تأیید، کد دیباگ را پاک می‌کنم تا در production نماند.

روش سوم: بررسی اثر عملی

روش سوم که در پروژه‌های واقعی اهمیت بالایی دارد، بررسی اثر عملی رویداد است. مثلاً اگر مشکل مربوط به بکاپ بود، بررسی می‌کنم که بکاپ‌های جدید در فاصله‌های منظم ذخیره می‌شوند. اگر مربوط به ایمیل بود، بررسی می‌کنم که ایمیل‌های زمان‌بندی‌شده به‌موقع می‌رسند. اگر مربوط به اسکن امنیتی بود، لاگ‌های افزونه امنیتی را بررسی می‌کنم. فهرست کامل افزونه‌های بکاپ که در تأیید این مرحله به کارم می‌آید در بهترین افزونه‌های بکاپ وردپرس برای پشتیبان‌گیری آمده است.

رفع مشکل بدون تأیید، فقط یک تعطیلی موقت است؛ تأیید سه‌روزه، ضمانت پایداری بلندمدت.

پرسش‌های پرتکرار درباره خرابی کرون وردپرس

چرا بکاپ خودکار من با تاریخ نامنظم ذخیره می‌شود؟ چون WP-Cron به ترافیک کاربران وابسته است. اگر سایت شما در ساعت‌هایی که بکاپ تنظیم شده بازدید ندارد، بکاپ به اولین بازدید بعدی موکول می‌شود و همین باعث نامنظم شدن تاریخ‌ها می‌شود. راه‌حل پایدار، مهاجرت به cron سیستمی است. برای روش دقیق، کرون وردپرس و زمان‌بندی خودکار کارها را بخوانید.

WP-CLI برای بررسی cron چه دستورهایی دارد؟ سه دستور اصلی wp cron event list، wp cron event run و wp cron event schedule در اکثر عیب‌یابی‌ها کافی هستند. اگر با WP-CLI آشنا نیستید، ابتدا نصبش کنید. روی اکثر هاست‌های اشتراکی، نصب دستی آن بیش از پنج دقیقه وقت نمی‌گیرد و ارزش سرمایه‌گذاری دارد.

چرا فهرست cron من پر از رویدادهای ناشناس است؟ احتمالاً رویدادهای یتیم از افزونه‌های حذف‌شده هستند. با مقایسه نام رویدادها با پیشوند افزونه‌های فعال، می‌توانید این رویدادها را شناسایی کنید. پاک‌سازی آن‌ها معمولاً سرعت پیشخوان و بارگذاری سایت را محسوس بهتر می‌کند. روش دقیق پاک‌سازی در مدیریت داده‌های موقت در وردپرس آمده است.

آیا افزونه‌های کش می‌توانند WP-Cron را از کار بیندازند؟ بله، در بعضی سناریوها. اگر افزونه کش، درخواست‌های داخلی cron را کش کند یا فایل wp-cron.php را در فهرست مسدودها قرار دهد، WP-Cron از کار می‌افتد. برای تشخیص، افزونه کش را موقتاً غیرفعال کنید و اثرش را ببینید. در صورت تأیید، قوانین استثنا در افزونه کش اضافه کنید.

چرا در آرایه cron چندین رویداد با نام یکسان دیده می‌شود؟ این نشانه رویدادهای تکراری است که از یک الگوی کد اشتباه ریشه می‌گیرد. اگر در ثبت رویداد شرط wp_next_scheduled رعایت نشده باشد، هر بار که افزونه لود شود، رویداد جدید اضافه می‌شود. راه‌حل، اصلاح کد افزونه و پاک‌سازی رویدادهای تکراری موجود است.

آیا می‌توانم بدون دسترسی SSH عیب‌یابی کنم؟ بله، ولی محدودیت‌هایی دارد. بدون SSH، نمی‌توانید WP-CLI اجرا کنید و باید به دسترسی مستقیم به دیتابیس و پنل هاست اکتفا کنید. برای تشخیص اولیه کافی است ولی برای رفع بعضی مشکلات، دسترسی SSH ارزش بالایی دارد. اگر روی هاست اشتراکی هستید و دسترسی SSH محدود است، می‌توانید از پنل cPanel برای دیدن cron سیستمی و از phpMyAdmin برای بررسی آرایه cron استفاده کنید.

آیا cron خراب، روی امنیت سایت اثر دارد؟ بله. اگر افزونه امنیتی شما از cron برای اسکن دوره‌ای استفاده می‌کند، خرابی cron باعث می‌شود اسکن انجام نشود. همچنین اگر بکاپ خودکار انجام نشود و سایت در فاصله‌ای هک شود، امکان بازگشت به نسخه سالم وجود ندارد. این دو دلیلی است که در همه پروژه‌ها، پایش کرون را جدی می‌گیرم. تحلیل امنیتی کامل در راهنمای امنیت وردپرس برای مبتدیان آمده است.

چرا WP-CLI خطا می‌دهد ولی از پنل cron کار می‌کند؟ این سناریو وقتی رخ می‌دهد که WP-Cron غیرفعال باشد ولی cron سیستمی به‌درستی تنظیم شده باشد. WP-CLI مستقل از این دو مکانیزم کار می‌کند و اگر رویدادی را به‌اجرا فراخوانی کنید، مستقیماً اجرا می‌شود. اگر در پنل cPanel، cron سیستمی وجود دارد ولی دستور WP-CLI رویداد را نمی‌بیند، یعنی وردپرس در مسیر درستی از cron سیستمی مطلع نیست.

آیا باید قبل از هر پاک‌سازی، رویدادها را ذخیره کنم؟ بله، این عادت حرفه‌ای است. قبل از هر عملیات پاک‌سازی روی آرایه cron، یک فایل CSV از رویدادها با دستور wp cron event list --format=csv بگیرید. اگر بعد از پاک‌سازی، مشکل جدیدی ظاهر شد، با مقایسه فایل CSV و وضعیت فعلی، می‌توانید سریع تشخیص دهید چه چیزی از دست رفته است. بکاپ دیتابیس هم قبل از پاک‌سازی بزرگ الزامی است.

آیا WP-Cron روی همه نسخه‌های وردپرس یکسان کار می‌کند؟ در کل بله، ولی جزئیات متفاوت است. از وردپرس ۵.۱، تابع wp_doing_cron() اضافه شده که به افزونه‌ها امکان تشخیص محیط cron را می‌دهد. از وردپرس ۵.۳، مدیریت timezone در توابع cron بهبود یافته. اگر روی نسخه قدیمی وردپرس هستید و با مشکل timezone مواجهید، ارتقا به نسخه جدید می‌تواند راه‌حل باشد.

چطور بفهمم رویداد در حال اجراست یا در صف مانده؟ با دستور wp cron event list، فیلد next_run_relative زمان اجرای بعدی را نشان می‌دهد. اگر این زمان در گذشته باشد و به‌طور مکرر دیده شود، یعنی رویداد در صف مانده و اجرا نمی‌شود. اگر در حالت عادی رویدادها به‌موقع اجرا می‌شوند ولی یک رویداد خاص در صف می‌ماند، احتمالاً آن رویداد با خطایی مواجه است که در debug.log ثبت شده است.

آیا cron ناسالم، روی سرعت بازدید کاربران اثر دارد؟ بله، به‌طور مستقیم. اگر آرایه cron بزرگ باشد، هر بازدید کاربر شامل سریالایز و دیسریالایز آن آرایه می‌شود. در پروژه‌هایی که آرایه cron به هزاران ردیف رسیده، کاهش سرعت بازدید تا دو برابر دیده شده است. این اثر روی Core Web Vitals (شاخص‌های اصلی وب) و رتبه سایت در گوگل هم بازتاب پیدا می‌کند.

رویدادها را جدی بگیرید، پیش از آنکه هزینه‌ساز شوند

در پایان این مسیر، یک حقیقت را باید بپذیریم: مشکلات WP-Cron، خودشان را تا زمانی که درد واقعی در سایت ایجاد نشود، نشان نمی‌دهند. بکاپ‌های از دست رفته، ایمیل‌های نرسیده و اسکن‌های انجام‌نشده، تا وقتی که یک حادثه‌ای رخ ندهد، در سکوت باقی می‌مانند. تجربه من این است که هر ساعت سرمایه‌گذاری روی پایش دوره‌ای کرون، در بلندمدت چند برابر برمی‌گردد و این برگشت، در مواقع حساس مثل بازگردانی از بکاپ یا بررسی حادثه امنیتی، به‌طور محسوس دیده می‌شود.

عادات ساده‌ای که در همه پروژه‌های خودم پیاده می‌کنم، ممکن است در ابتدا اضافه به نظر برسند ولی در عمل، پشتوانه پایداری سایت هستند. اول، هر بار که یک سایت مشتری تحویل می‌دهم، یک جلسه پایش کرون برای خودم می‌گذارم و رویدادهای موجود را مستند می‌کنم. دوم، در تقویم کاری خودم، ماهانه یک مرور سریع روی WP-Cron سایت‌های فعال دارم. سوم، هر بار که افزونه‌ای را حذف می‌کنم، پیش از حذف، رویدادهای cron آن را یادداشت می‌کنم و اگر هنگام حذف، به‌درستی پاک نشوند، به‌طور دستی پاک می‌کنم.

اگر در ابتدای مسیر یادگیری هستید، سه تمرین را پیشنهاد می‌کنم. اول، روی یک سایت تستی، یک افزونه ساده بسازید که بدون شرط wp_next_scheduled رویداد ثبت کند و بعد از چند روز، تعداد رویدادهای تکراری را در آرایه cron ببینید. دوم، با WP-CLI، رویدادهای یک سایت فعال را مستند کنید و بعد از یک هفته، دوباره همان مستند را بگیرید تا روند تغییرات را ببینید. سوم، یک بار در محیط staging، WP-Cron را به‌طور کامل غیرفعال کنید و ببینید در طول یک هفته چه اتفاقی برای سایت می‌افتد. این سه تجربه، درک عمیقی از اهمیت این لایه به شما می‌دهد که هیچ مقاله‌ای جایگزینش نمی‌شود.

اگر تجربه‌ای از عیب‌یابی WP-Cron در پروژه‌ای واقعی دارید — چه با موفقیت، چه پر از دام — در دیدگاه بنویسید. آن تجربه برای کسی که همین امروز با علائم مشابه مواجه شده و نمی‌داند از کجا شروع کند، ارزشمندتر از هر مستند رسمی است. 🔧