آموزش وردپرس

خطای ۵۰۰ در وردپرس: از لاگ سرور تا افزونهٔ مقصر

وحید بهنام26 دقیقه مطالعه5 بازدید0 دیدگاه
wp-500-hero

خطای ۵۰۰ وردپرس (WordPress 500 Internal Server Error) یعنی درخواست به سرور رسیده و اجرای آن شکسته است؛ این پاسخ را وب‌سرور یا PHP می‌سازد، نه خود وردپرس. پیام «Internal Server Error» عمداً کلی است و نام فایل، افزونه یا شمارهٔ خط را نشان نمی‌دهد، چون نمایش آن مسیرهای مطلق و نسخهٔ کتابخانه‌ها را لو می‌دهد. علت واقعی در یکی از سه جا نوشته شده: لاگ وب‌سرور، لاگ PHP، یا فایل debug.log خود وردپرس. کار درست حدس زدن نیست؛ خواندن همان لاگ و تطبیق زمان است.

خطای ۵۰۰ چه معنایی دارد و چرا پیام دقیق نمی‌دهد

کد ۵۰۰ در استاندارد HTTP یعنی «سرور درخواست را دریافت کرد، ولی یک شرط پیش‌بینی‌نشده نگذاشت آن را کامل کند». این تعریف عمداً مبهم است: ۵۰۰ چترِ همهٔ خطاهایی است که سرور نمی‌تواند دقیق‌تر دسته‌بندی کند. تفاوت بنیادی‌اش با ۴۰۴ یا ۴۰۵ این است که در خانوادهٔ ۴xx مشکل در درخواست است و در خانوادهٔ 5xx مشکل در سمت اجرای سرور.

هفت گام تشخیص خطای ۵۰۰۱۲۳۴۵۶۷خطا را ببینحالت دیباگلاگ را بخوانافزونه‌ها را قطع کنقالب را عوض کنحافظه و PHPبازیابی از بکاپ
هفت گام تشخیص، از دیدن خطا تا بازیابی

چه کسی ۵۰۰ را می‌فرستد؟ معمولاً وب‌سرور (Apache، LiteSpeed، Nginx) یا PHP-FPM، و گاهی یک افزونه که خودش با wp_die() کد ۵۰۰ می‌فرستد. ولی در حالت عادی خود وردپرس این کد را نمی‌سازد؛ وردپرس کد PHP اجرا می‌کند، و وقتی آن کد در میانهٔ کار می‌میرد، فرایندی که بالاتر ایستاده مرگش را به ۵۰۰ ترجمه می‌کند. پس ترکیب «خطای ۵۰۰ وردپرس» یعنی «وردپرس در حال اجرا از کار افتاد»، نه «وردپرس خطایی تولید کرد». همین یک جمله، نیمی از مسیر عیب‌یابی را عوض می‌کند: دنبال پاسخ در تنظیمات وردپرس نگردید، دنبالش در لایه‌ای بگردید که وردپرس را اجرا می‌کند.

دلیل عمومی بودن پیام، سندرم افشای اطلاعات است. یک traceback کامل مسیر مطلق (/home/username/public_html)، نام کاربری دیتابیس، نسخهٔ کتابخانه‌ها و فهرست افزونه‌ها را بیرون می‌ریزد. به همین دلیل میزبان‌ها display_errors را خاموش می‌گذارند و یک صفحهٔ ثابت نشان می‌دهند. این تصمیم از نظر امنیتی درست است و از نظر عیب‌یابی یک دیوار می‌سازد؛ تنها راه عبور از دیوار، لاگ است، نه صفحه.

از وردپرس ۵.۲ به بعد شکل دومی هم پیدا شد: پیام There has been a critical error on this website که خود وردپرس تولید می‌کند (فیتال‌ارور هندلر و Recovery Mode). در همین حالت وردپرس نامه‌ای به ایمیل مدیر سایت می‌فرستد که فایل و شمارهٔ خط در آن است و پیوند ورود به حالت بازیابی را هم دارد. اگر سایت ایمیل نمی‌فرستد، این نامه هرگز نمی‌رسد و شما یک مسیر تشخیص آماده را از دست داده‌اید.

یک تمایز مهم آخر: ۵۰۰ با «سایت هک شد»، «هاست قطع شد» یا «DNS خراب شد» یکی نیست. هر سه می‌توانند همزمان رخ دهند، ولی درمان‌شان کاملاً جداست. قبل از هر کاری، باید مطمئن شوید پاسخ واقعاً ۵۰۰ است؛ بخش بعدی همین را می‌سنجد.

تفاوت ۵۰۰، ۵۰۲، ۵۰۳ و ۵۰۴ در یک نگاه

چهار کد شبیه هم دیده می‌شوند و کاربر معمولاً یکی را به‌جای دیگری می‌خواند. تفاوتشان در این است که «کدام حلقه شکست»؛ و همین، اولین سرنخ مسیر است.

چهار کد 5xx و جایی که شکست می‌خورند
کدمعنیکجا شکست خوردنشانهٔ عملی
500خطای پیش‌بینی‌نشده در اجرای درخواستPHP یا پیکربندی وب‌سرور روی همان سرورصفحهٔ کوتاه یا خالی؛ لاگ PHP معمولاً یک ردیف دارد
502پروکسی از سرویس پشتی پاسخ معتبر نگرفتبین وب‌سرور و PHP-FPMلاگ FPM می‌گوید فرایند مرد یا استخر پر شد
503سرویس موقتاً در دسترس نیستخود سایت، حالت نگهداری، یا محدودیت منابعاغلب هدر Retry-After دارد
504سرویس پشتی در زمان مجاز پاسخ ندادانتظار پروکسی برای پاسخ PHPزمان پاسخ بلند؛ لاگ timeout دارد

دو کد دیگر هم در عمل زیاد دیده می‌شوند و در این چهارتایی جا نمی‌شوند: 508 که در پلن‌های اشتراکی با لایهٔ محدودسازی منابع ظاهر می‌شود، و خانوادهٔ 52x که مخصوص لبهٔ Cloudflare است (پاسخ نامفهوم، سرور خاموش، تایم‌اوت). اگر یکی از این‌ها را دیدید، مسئله یک لایه عقب‌تر از کد است و درمانش هم آنجاست. مقایسهٔ کدهای ۴xx هم همین منطق را دارد؛ مثلاً خطای ۴۰۵ Method Not Allowed همیشه یعنی متد درخواست پذیرفته نیست، نه اینکه فایل غایب است.

کد واقعی را از هدر بخوانید، نه از ظاهر صفحه. یک صفحهٔ خطای سفارشی، یک کش خراب یا یک WAF می‌تواند صفحه‌ای دوستانه نشان دهد که کد اصلی نیست. با curl و نمایش کد وضعیت، حقیقت را ببینید. اگر سایت پشت CDN است، یک درخواست مستقیم به origin هم بگیرید تا بدانید خطا از سرور می‌آید یا از لبه. و اگر پاسخ 503 با هدر Retry-After بود، مسیر درمانش با ۵۰۰ یکی نیست؛ جزئیاتش در کد ۵۰۳ و حالت نگهداری وردپرس آمده است.

اولین قدم: کجای سایت خطا می‌دهد؛ کل سایت یا یک صفحه

پیش از هر تغییری، دامنهٔ خطا را اندازه بگیرید. این کار یک دقیقه وقت می‌گیرد و بیشتر از هر حدسی جهت می‌دهد. چهار آدرس را بزنید: صفحهٔ اصلی، یک برگهٔ داخلی، ورود به پیشخوان، و یک فایل ایستا که PHP در ساختنش دخالتی ندارد.

درخت تصمیم: کل سایت یا یک صفحه
این یک پرسش، نیمی از مسیر تشخیص را کوتاه می‌کند
BASH
for u in "/" "/about/" "/wp-login.php" "/wp-admin/" "/wp-includes/js/wp-emoji-release.min.js"; do
  printf "%-46s " "$u"
  curl -s -o /dev/null -w "http=%{http_code} time=%{time_total}sn" "https://example.com$u"
done

پنج الگوی پاسخ دارید و هر کدام یک لایه را نشانه می‌گیرد:

  • فایل ایستا سالم است، صفحه‌های PHP خطا می‌دهند. خطا در لایهٔ PHP است: افزونه، قالب، حافظه، یا دیتابیس. این شایع‌ترین حالت است و بقیهٔ مقاله همین را دنبال می‌کند.
  • همه چیز، حتی فایل ایستا، ۵۰۰ می‌دهد. خطا در پیکربندی وب‌سرور است: یک دستور نامعتبر در .htaccess، مجوز فایل، یا ناسازگاری پیکربندی. دیگر وقت تلف کردن روی افزونه‌ها بی‌فایده است.
  • فقط یک صفحه یا یک نوشته. خطا در محتواست: یک شورت‌کد افزونهٔ حذف‌شده، یک بلوک خراب، یا صفحه‌ای که حافظهٔ بیشتری می‌خواهد. بقیهٔ سایت سالم بودن، خودش یک سرنخ قوی است.
  • فقط پیشخوان. معمولاً حافظه است، چون پیشخوان سقف حافظهٔ بالاتری می‌خواهد و افزونه‌های سنگین‌تری را بار می‌کند؛ یا یک صفحهٔ ادمین مشخص در یک افزونه.
  • فقط فرم‌ها و درخواست‌های POST. سمت امنیت و لبه: WAF، mod_security، یا کوکی و نشست خراب. درخواست GET سالم است و POST می‌شکند؛ این تفکیک را حتماً ثبت کنید.

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

و نکته‌ای که در هیچ‌کدام از راهنماهای فارسی به‌عنوان ابزار ندیدم: زمان پاسخ خودش یک تشخیص است. عدد time= در همان خروجی بالا سه حالت را از هم جدا می‌کند.

امضای زمانی خطای ۵۰۰
زمان پاسخمعنای محتملکجا را بگردید
زیر یک ثانیهخطای نحوی یا fatal در همان ابتدای بوت؛ کد حتی به نیمهٔ راه نرسیدهردیف PHP Parse error یا Uncaught Error در لاگ
چند ثانیه تا حدود ۳۰ ثانیهانتظار روی دیتابیس، یک API بیرونی، یا قفل جدولخطای MySQL، تایم‌اوت اتصال، افزونهٔ همگام‌سازی
نزدیک یا بیش از ۳۰ ثانیهرسیدن به سقف زمان اجرا یا سقف حافظهMaximum execution time یا Allowed memory size
فوری و خالی، بدون هیچ ردیف PHPدرخواست پیش از رسیدن به PHP رد شدهپیکربندی وب‌سرور، مجوز فایل، WAF

این جدول را قبل از خواندن لاگ پر کنید. اگر خطا زیر یک ثانیه رخ می‌دهد، جست‌وجوی «حافظه» وقت تلف کردن است؛ و اگر بیست ثانیه طول می‌کشد و بعد ۵۰۰ می‌شود، خاموش کردن افزونه‌ها هم بی‌فایده است.

خواندن لاگ سرور: error_log، لاگ هاست و پیام‌های PHP

لاگ، جایی است که ۵۰۰ نام خودش را می‌گوید. محلش به نوع هاست بستگی دارد و معمولاً یکی از این‌هاست: در سی‌پنل بخش Metrics و سپس Errors؛ فایل error_log در ریشهٔ سایت و گاهی یک پوشه بالاتر؛ در سرور اختصاصی با Apache مسیر /var/log/apache2/error.log و با Nginx مسیر /var/log/nginx/error.log؛ و در PHP-FPM لاگ استخر که اغلب جدا از لاگ وب‌سرور است. اگر هیچ‌کدام را نمی‌بینید، سؤال درست از پشتیبانی این است: «لاگ خطای PHP این اکانت کجاست؟» — این سؤال را همهٔ میزبان‌ها جواب می‌دهند.

پیام لاگ و معنای واقعی آن
چهار پیام پرتکرار و علتی که پشتشان است
BASH
# آخرین ردیف‌های خطای PHP در ریشهٔ سایت
tail -n 120 /home/username/public_html/error_log

# فقط خطاهای مهلک و حافظه، برای یک نگاه سریع
grep -n -E "PHP Fatal error|PHP Parse error|memory size|execution time" 
  /home/username/public_html/error_log | tail -n 30

# تطبیق با لاگ دسترسی: چه آدرسی، چه ساعتی، با چه کدی
awk '$9 == 500 {print $4, $7, $9}' /home/username/logs/access_log | tail -n 20

سه تله در خواندن لاگ هست که وقت زیادی می‌سوزاند. اول، منطقهٔ زمانی: لاگ سرور اغلب UTC است و ساعت شما محلی؛ اگر ساعت محلی را جست‌وجو کنید، ردیف‌های بی‌ربط پیدا می‌کنید. زمان خطا را با ثانیه و با ذکر منطقه یادداشت کنید. دوم، چرخش لاگ: خطای دیروز در error_log.1 یا فایل فشرده است، پس با zgrep هم بگردید. سوم، تکرار: خطایی که هزار بار تکرار شده، معمولاً علت نیست، نشانه است؛ تعدادش را بشمارید و یک نمونهٔ کامل را بخوانید.

نقشهٔ لاگ: از پیام به علت
پیام در لاگعلت محتمل
PHP Fatal error: Uncaught Error: Call to undefined function … in /wp-content/plugins/…افزونه؛ آدرس فایل، خودِ مقصر را نام برده است
PHP Parse error: syntax error, unexpected … in functions.phpکد دستی در قالب؛ یک ویرگول یا آکولاد کم یا زیاد
PHP Fatal error: Allowed memory size of … bytes exhaustedسقف حافظهٔ PHP؛ بخش «خطای حافظه» را ببینید
PHP Fatal error: Maximum execution time of 30 seconds exceededسقف زمان اجرا؛ کار سنگین یا انتظار روی یک سرویس کند
Invalid command 'php_value' یا AH00526دستور نامعتبر در .htaccess؛ کل سایت را می‌خواباند
Permission denied در مسیر یک فایل PHPمجوز یا مالکیت فایل؛ نه ۶۴۴/۷۵۵
WordPress database error / Table … is marked as crashedدیتابیس؛ جدول نیاز به تعمیر دارد
upstream prematurely closed connection و کد ۵۰۲فرایند PHP-FPM مرده یا استخر پر شده — این ۵۰۰ نیست

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

حالت دیباگ وردپرس و آنچه در wp-config باید روشن شود

حالت دیباگ، خطاهای PHP را که وردپرس مدیریت می‌کند به فایل می‌نویسد بدون اینکه به بازدیدکننده نشان دهد. سه ثابت لازم است و باید بالای خط /* That's all, stop editing! Happy publishing. */ در wp-config.php قرار بگیرد.

PHP
define( 'WP_DEBUG', true );          // حالت دیباگ را روشن می‌کند
define( 'WP_DEBUG_LOG', true );      // خطاها را در wp-content/debug.log می‌نویسد
define( 'WP_DEBUG_DISPLAY', false ); // خطاها به بازدیدکننده نشان داده نشوند
@ini_set( 'display_errors', 0 );     // همان کار، در سطح PHP

سه نکته که بیشتر اشتباه فهمیده می‌شوند. اول، روشن کردن WP_DEBUG به‌تنهایی سطح گزارش را به همهٔ خطاها و هشدارها می‌برد؛ حتی هشدارهایی که سایت را نمی‌شکنند. پس لاگ شلوغ می‌شود و شما نباید هر ردیف را علت ۵۰۰ بدانید. دوم، WP_DEBUG_LOG فقط وقتی کار می‌کند که پوشهٔ wp-content برای PHP قابل نوشتن باشد؛ اگر فایل debug.log ساخته نشد، اول مجوز و مالکیت را چک کنید. سوم، اگر درخواست با خطای ۵۰۰ می‌شکند و debug.log هم خالی می‌ماند، یعنی خطا پیش از بارگذاری وردپرس رخ داده است: اتصال دیتابیس، فایل هسته، یا پیکربندی سرور. در این حالت به لاگ وب‌سرور برگردید.

امنیت این کار دو شرط دارد. فایل debug.log به‌طور پیش‌فرض داخل پوشهٔ عمومی است و هر کسی می‌تواند آدرسش را باز کند؛ پس دسترسی‌اش را ببندید (مجوز ۶۰۰ و یک قاعدهٔ deny) و بعد از تشخیص پاکش کنید. و دیباگ را روی سایت زنده روشن نگذارید؛ مستندات وردپرس هم آن را برای نسخهٔ آزمایشی می‌خواهد، نه سایت در دسترس عموم. کافی است چند دقیقه روشن بماند تا خطا تکرار شود، بعد خاموش.

یک تنظیم مرتبط هم هست که خیلی‌ها نمی‌شناسند: از وردپرس ۵.۲، فیتال‌ارور هندلر جلوی صفحهٔ سفید را می‌گیرد و به‌جایش پیام «critical error» و حالت بازیابی را می‌آورد. اگر برای دیدن خطای خام به صفحه نیاز دارید، WP_DISABLE_FATAL_ERROR_HANDLER را فقط در محیط آزمایشی روشن کنید؛ روی سایت زنده این کار یعنی برگشتن صفحهٔ سفید برای بازدیدکننده.

پیدا کردن افزونهٔ مقصر بدون دسترسی به پیشخوان

وقتی پیشخوان باز نمی‌شود، تنها راه خاموش کردن افزونه‌ها رابط کاربری نیست. سه راه دارید و ترتیبشان مهم است.

راه اول: لاگ. اگر لاگ آدرسی داخل /wp-content/plugins/ نام برده باشد، همان افزونه مقصر است و هیچ آزمون و خطایی لازم نیست. این حالت شایع‌تر از آن است که تصور می‌شود؛ اول لاگ را کامل بخوانید، بعد دست به پوشه‌ها ببرید.

راه دوم: جست‌وجوی دودویی، نه یکی‌یکی. اگر لاگ نامی نبرد، پوشهٔ plugins را کامل جابه‌جا کنید تا ببینید سایت بالا می‌آید یا نه. اگر بالا آمد — یعنی مقصر داخل پوشه است — نیمی از پوشه‌ها را برگردانید و تست کنید. با این روش، ۱۶ افزونه در چهار مرحله پیدا می‌شود، نه در شانزده مرحله.

BASH
cd /home/username/public_html/wp-content
mv plugins plugins-off     # همه خاموش؛ سایت را تست کنید
mkdir plugins              # پوشهٔ خالی، تا وردپرس دوباره برایش مسیر داشته باشد

# نیمهٔ اول را برگردانید و سایت را تست کنید
ls plugins-off | head -n 8 | while read d; do mv "plugins-off/$d" plugins/; done
ls plugins                 # فهرست نیمهٔ برگشته

راه سوم: از دیتابیس. فهرست افزونه‌های فعال در جدول wp_options و در ردیف active_plugins نگه‌داری می‌شود؛ یک مقدار سریالایز که با خالی کردنش همهٔ افزونه‌ها خاموش می‌شوند. مزیتش این است که لازم نیست نام پوشه‌ها را بدانید.

SQL
-- اول بخوانید و مقدار را در یک فایل متنی ذخیره کنید
SELECT option_value FROM wp_options WHERE option_name = 'active_plugins';

-- بعد خالی کنید؛ a:0:{} یعنی «آرایهٔ خالی» و همان چیزی است که وردپرس می‌فهمد
UPDATE wp_options SET option_value = 'a:0:{}' WHERE option_name = 'active_plugins';

دو هشدار روی این کوئری. اول، پیشوند جدول همیشه wp_ نیست؛ مقدار $table_prefix را در wp-config.php ببینید و نام جدول را با همان بسازید. دوم، این تغییر داده است، نه تنظیم؛ قبلش از جدول wp_options نسخه بگیرید، وگرنه برای برگرداندن فهرست افزونه‌ها باید حدس بزنید کدام‌ها فعال بوده‌اند.

اگر به SSH دسترسی دارید، WP-CLI میان‌بر قابل اعتمادتری است. مهم‌ترین پرچمش --skip-plugins است: وقتی یک افزونه در بوت سایت را می‌کشد، همان دستور هم می‌میرد؛ با این پرچم، ابزار بدون بار کردن افزونه‌ها اجرا می‌شود. راهنمای نصب و کار با آن در WP-CLI روی هاست اشتراکی آمده است.

BASH
wp --skip-plugins --skip-themes plugin list --status=active
wp --skip-plugins --skip-themes plugin deactivate --all
wp plugin activate plugin-name    # برگرداندن یکی‌یکی، با تست بعد از هر مورد

و یک تله که خودش یک مقاله است: فهرست active_plugins شامل mu-plugins نیست. افزونه‌های داخل wp-content/mu-plugins و فایل‌های drop-in مثل object-cache.php و advanced-cache.php همیشه بار می‌شوند، در فهرست افزونه‌ها دیده نمی‌شوند و با خالی کردن جدول هم خاموش نمی‌شوند. اگر «همهٔ افزونه‌ها را خاموش کردم ولی هنوز ۵۰۰ است»، تقریباً همیشه یکی از همین‌ها مقصر است. آن‌ها را دستی از پوشه بیرون ببرید، نه با دیتابیس.

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

خطای حافظه و محدودیت PHP

شکل این خطا در لاگ ثابت است و عدد داخلش مهم است: PHP Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 20480 bytes) in /home/username/public_html/wp-includes/…. عدد اول سقف فعلی است (این یکی ۲۵۶ مگابایت)، عدد دوم مقداری که اسکریپت در همان لحظه خواسته، و مسیر فایل معمولاً می‌گوید چه کسی حریص بوده.

وردپرس دو سقف جدا دارد: WP_MEMORY_LIMIT برای بازدیدکننده که پیش‌فرضش ۴۰ مگابایت است، و WP_MAX_MEMORY_LIMIT برای پیشخوان که پیش‌فرضش ۲۵۶ مگابایت است. همین تفاوت توضیح می‌دهد چرا سایت برای مهمان سالم است و پیشخوان ۵۰۰ می‌دهد.

PHP
define( 'WP_MEMORY_LIMIT', '256M' );     // سقف سمت بازدیدکننده
define( 'WP_MAX_MEMORY_LIMIT', '512M' ); // سقف پیشخوان

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

و اینجا تلهٔ اصلی است: دستور php_value در htaccess. سال‌ها روش استاندارد این بود که در .htaccess بنویسید php_value memory_limit 256M. این دستور فقط زیر mod_php کار می‌کند. اگر میزبان به PHP-FPM یا LiteSpeed مهاجرت کرده باشد، همان خط باعث خطای Invalid command 'php_value' می‌شود و کل سایت — نه یک صفحه — ۵۰۰ می‌دهد. علامتش هم این است که خطا از روزی شروع شده که هیچ کدی عوض نشده و فقط تنظیمات سرور تغییر کرده. راه درست، فایل تنظیمات PHP در همان پوشه است:

INI
; .user.ini در ریشهٔ سایت، برای PHP-FPM و LiteSpeed
memory_limit = 256M
max_execution_time = 120
upload_max_filesize = 64M
post_max_size = 64M

افزایش سقف حافظه همیشه درمان نیست؛ اگر اسکریپتی ۵۱۲ مگابایت می‌خواهد، با ۱۰۲۴ مگابایت هم روزی به سقف می‌رسد. مصرف‌کنندهٔ واقعی را پیدا کنید. شایع‌ترینش جدول wp_options است: هر ردیفی که autoload = yes دارد در هر درخواست خوانده می‌شود، و یک ردیف چند مگابایتی از یک افزونهٔ کند کافی است تا سایت هم کند شود هم به سقف بخورد. روش پاک‌سازی و اندازه‌گیری در autoload و حجم wp_options آمده است.

سقف زمان اجرا هم همین رفتار را دارد و پیامش جداست: Maximum execution time of 30 seconds exceeded. این خطا بیشتر در پیشخوان دیده می‌شود، جایی که ایمپورت، پشتیبان‌گیری یا بازسازی تصاویر اجرا می‌شود؛ و بدترین حالتش این است که کار نیمه‌کاره بماند. برای کارهای سنگین، درخواست وب ابزار اشتباهی است؛ از خط فرمان استفاده کنید که سقف زمانی ندارد.

خطای دیتابیس: کاربر، رمز، یا افتادن جدول

خطای دیتابیس سه چهره دارد و هر سه می‌توانند به ۵۰۰ یا صفحهٔ سفید برسند. چهرهٔ اول پیام آشنای Error establishing a database connection است. چهرهٔ دوم WordPress database error با نام جدول. چهرهٔ سوم MySQL server has gone away که یعنی اتصال وسط کار بریده شده. اولی تقصیر تنظیمات است، دومی تقصیر داده، سومی تقصیر منابع.

برای چهرهٔ اول، پنج مقدار را در wp-config.php ببینید: DB_NAME، DB_USER، DB_PASSWORD، DB_HOST و $table_prefix. دو اشتباه رایج: رمز دیتابیس از پنل هاست عوض شده ولی در فایل مانده، و تغییر localhost به 127.0.0.1 یا برعکس. این دو یکی نیستند؛ اولی از سوکت یونیکس استفاده می‌کند و دومی از TCP، و بعضی میزبان‌ها فقط یکی را قبول دارند. اگر سایت تازه منتقل شده، همین یک مقدار می‌تواند کل سایت را بخواباند.

چهرهٔ دوم جدول است. اگر یک جدول خراب شده باشد، معمولاً فقط صفحه‌هایی که به آن جدول نیاز دارند می‌شکنند و بقیه سالم‌اند — و همین، تشخیص را ساده می‌کند. تعمیر از phpMyAdmin با دو دستور انجام می‌شود:

SQL
CHECK TABLE wp_posts, wp_postmeta, wp_options, wp_usermeta;
REPAIR TABLE wp_posts;

یک واقعیت که در راهنماها کمتر گفته می‌شود: REPAIR TABLE روی جدول‌های InnoDB معمولاً پیام «موتور این جدول تعمیر را پشتیبانی نمی‌کند» برمی‌گرداند. یعنی اگر جدول‌های شما InnoDB هستند — که در وردپرس امروز پیش‌فرض است — این دستور مشکل را حل نمی‌کند و باید داده را از نسخهٔ پشتیبان بازگردانید یا جدول را بازسازی کنید. برای MyISAM همان دستور کار می‌کند. قبل از هر کاری نوع موتور جدول را ببینید، وگرنه دو ساعت دنبال دستوری می‌دوید که از اول نمی‌توانست جواب بدهد.

راه دوم، ابزار خود وردپرس است. با روشن کردن یک ثابت، صفحهٔ تعمیر در پیشخوان بدون نیاز به ورود باز می‌شود و همهٔ جدول‌ها را بررسی و بهینه می‌کند:

PHP
define( 'WP_ALLOW_REPAIR', true ); // فقط تا پایان تعمیر

بعد از آن آدرس /wp-admin/maint/repair.php را باز کنید و گزینهٔ تعمیر را بزنید. دو شرط: این صفحه وقتی ثابت روشن است برای همه باز است، پس در همان ساعت خاموشش کنید؛ و اگر جدول واقعاً خراب باشد، تعمیر ممکن است ردیف‌هایی را قربانی کند — نسخهٔ پشتیبان قبلش واجب است.

چهرهٔ سوم، منابع است. MySQL server has gone away معمولاً یعنی بستهٔ داده بزرگ‌تر از max_allowed_packet بوده یا اتصال از wait_timeout گذشته. و خطای «too many connections» یعنی سقف اتصال همزمان پر شده؛ آن یکی تقصیر شما نیست و باید از هاست پرسیده شود.

خطای فایل‌های قالب و child theme

قالب دو جور سایت را می‌خواباند: با خطای نحوی در فایل PHP، یا با صدا زدن تابعی که دیگر وجود ندارد. حالت دوم در قالب فرزند (child theme) شایع‌تر است: قالب فرزند تابعی از قالب والد را صدا می‌زند، والد به‌روزرسانی می‌شود و آن تابع تغییر نام می‌دهد یا حذف می‌شود، و از آن لحظه هر صفحه ۵۰۰ می‌دهد. پیامش هم صریح است: Call to undefined function و مسیر فایل فرزند.

قبل از هر تغییری، فایل را از نظر نحوی چک کنید. یک ویرگول جاافتاده در functions.php کل سایت را می‌خواباند، و پنل مدیریت فایل هیچ خطای نحوی نشان نمی‌دهد؛ فقط ذخیره می‌کند. اگر SSH دارید، یک دستور کافی است:

BASH
php -l /home/username/public_html/wp-content/themes/my-child/functions.php
# No syntax errors detected  → فایل سالم است، مقصر جای دیگری است
# PHP Parse error: … on line 47 → همان خط را اصلاح کنید

برای برگشتن سریع سایت، قالب فعال را عوض کنید. دو راه دارد: پوشهٔ قالب را تغییر نام بدهید تا وردپرس به قالب پیش‌فرض برگردد، یا از دیتابیس دو ردیف را عوض کنید. راه دوم دقیق‌تر است، چون دقیقاً می‌دانید چه چیزی تغییر کرده:

SQL
SELECT option_name, option_value FROM wp_options
WHERE option_name IN ('template', 'stylesheet');

UPDATE wp_options SET option_value = 'twentytwentyfive'
WHERE option_name IN ('template', 'stylesheet');

دو نکته روی این کوئری: هر دو ردیف باید هم‌زمان عوض شوند، چون یکی نام پوشهٔ قالب والد است و دیگری قالب فعال؛ و اگر قالب فرزند فعال بوده، هر دو مقدار نام پوشهٔ فرزند را داشته‌اند، نه والد را. عوض کردن فقط یکی، ناسازگاری می‌سازد و همان ۵۰۰ را برمی‌گرداند.

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

وقتی هاست مقصر است

همهٔ ۵۰۰ها از کد نمی‌آیند. شش نشانه هست که می‌گوید باید به هاست زنگ بزنید، نه به کد: خطا در لاگ PHP هیچ ردیفی ندارد؛ پاسخ در ساعات پیک برمی‌گردد و در ساعت خلوت نیست؛ همان درخواست از شبکهٔ دیگری سالم است؛ خطا فقط روی درخواست‌های POST است؛ همهٔ سایت‌های PHP روی همان اکانت با هم می‌افتند؛ و پنل هاست از مصرف منابع یا رسیدن به سقف خبر می‌دهد.

مهم‌ترین مقصر سمت هاست، پر شدن استخر PHP-FPM است. هر استخر تعداد محدودی فرایند همزمان دارد (pm.max_children)؛ وقتی همه مشغول باشند، درخواست‌های تازه در صف می‌مانند، طول می‌کشند و بعد به ۵۰۲ یا ۵۰۴ — و گاهی به ۵۰۰ — می‌رسند. در لاگ استخر جمله‌ای شبیه «reached pm.max_children, consider raising» دیده می‌شود. این را خودتان نمی‌توانید درست کنید؛ باید میزبان سقف را بالا ببرد یا سایت را سبک‌تر کنید.

سه سقف دیگر هم همین رفتار را دارند: فضای دیسک، تعداد inode، و سقف منابع پلن اشتراکی که با لایهٔ محدودسازی اعمال می‌شود و معمولاً کد ۵۰۸ می‌دهد. برای اولی و دومی، دو دستور کافی است:

BASH
df -h /home/username    # فضای دیسک
df -i /home/username    # تعداد فایل‌ها (inode)؛ پر شدنش هم سایت را می‌خواباند

مقصر چهارم، لایهٔ امنیتی است. یک قاعدهٔ سخت‌گیر در WAF یا mod_security می‌تواند درخواستی را که از نظر شما سالم است رد کند؛ نشانه‌اش این است که GET جواب می‌دهد و POST می‌شکند، یا فقط درخواست‌های یک افزونهٔ مشخص رد می‌شوند. خاموش کردن کل WAF راه حل نیست؛ از هاست بخواهید لاگ audit را برای همان زمان و همان آدرس ببیند.

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

بازیابی سریع: بکاپ، بازگشت نسخه، و جایگزینی دستی

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

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

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

BASH
wp core verify-checksums
# اگر همه سالم بودند، هسته را دست نزنید؛ مقصر جای دیگری است

اگر خروجی فایل‌های تغییریافته را نشان داد، جایگزینی فقط دو پوشه را شامل می‌شود و به هیچ چیز دیگری دست نمی‌زند:

BASH
cd /home/username
curl -O https://wordpress.org/latest.tar.gz
tar -xzf latest.tar.gz

mv public_html/wp-admin public_html/wp-admin.old
mv public_html/wp-includes public_html/wp-includes.old
cp -a wordpress/wp-admin wordpress/wp-includes public_html/

# بعد از تست موفق، نسخه‌های قدیمی را پاک کنید
rm -rf public_html/wp-admin.old public_html/wp-includes.old

wp-config.php و wp-content هرگز جایگزین نمی‌شوند؛ اولی تنظیمات و اعتبارنامهٔ شماست و دومی محتوا، تصاویر و افزونه‌ها. اگر پوشهٔ wp-content را از نسخهٔ تازه کپی کنید، هم سایت را خالی کرده‌اید هم نسخهٔ پشتیبان را بی‌فایده.

یک بازماندهٔ رایج هم هست: فایل .maintenance در ریشهٔ سایت. وردپرس آن را هنگام به‌روزرسانی می‌سازد و اگر به‌روزرسانی نیمه‌کاره بماند، فایل هم می‌ماند. سایت در آن حالت پیام نگهداری نشان می‌دهد، و اگر هسته نیمه‌به‌روز شده باشد، ترکیب فایل‌های قدیم و جدید به ۵۰۰ می‌رسد. بعد از مطمئن شدن از سالم بودن هسته، فایل مانده را حذف کنید؛ توضیح کاملش در خطای حالت نگهداری وردپرس است.

آخرین قدم بازیابی، همان قدمی است که همه رد می‌کنند: دیباگ را خاموش کنید، debug.log را پاک کنید، و در سه خط بنویسید چه چیزی چه ساعتی خراب شد و چه چیزی درستش کرد. بدون این سه خط، شش ماه بعد همان حادثه از صفر شروع می‌شود.

چک‌لیست تشخیص در ده دقیقه

این ترتیب را عوض نکنید؛ هر قدم ورودی قدم بعدی را می‌سازد. کنار هر خط، نتیجه را بنویسید — عیب‌یابی با حافظه انجام نمی‌شود.

  1. دقیقهٔ ۰ تا ۱ — کد واقعی و دامنه. چهار آدرس را با curl بزنید و کد و زمان پاسخ را ثبت کنید. کل سایت یا یک صفحه؟ فایل ایستا هم می‌شکند؟
  2. دقیقهٔ ۱ تا ۲ — ساعت دقیق. زمان خطا را با ثانیه و منطقهٔ زمانی بنویسید و آخرین تغییر (افزونه، قالب، PHP، DNS) را کنارش یادداشت کنید.
  3. دقیقهٔ ۲ تا ۴ — لاگ. آخرین صد ردیف لاگ سرور را بخوانید و با جدول نقشهٔ لاگ تطبیق دهید. اگر نام فایل یا افزونه در لاگ بود، همان را باور کنید و از قدم پنجم شروع کنید.
  4. دقیقهٔ ۴ تا ۵ — دیباگ. اگر لاگ ساکت بود، سه ثابت دیباگ را روشن کنید، خطا را تکرار کنید، و debug.log را بخوانید. خالی ماندنش خودش یک پاسخ است: خطا پیش از وردپرس رخ می‌دهد.
  5. دقیقهٔ ۵ تا ۷ — ایزوله‌سازی. افزونه‌ها را با جست‌وجوی دودویی کنار بگذارید، بعد قالب. هر بار فقط یک تغییر، و بعد از هر تغییر همان آدرس خطادار را دوباره بزنید.
  6. دقیقهٔ ۷ تا ۸ — حافظه و زمان اجرا. سقف‌های مؤثر PHP را بخوانید و با پیام لاگ مقایسه کنید. اگر سقف با عدد داخل خطا یکی بود، افزایش در wp-config بی‌فایده است.
  7. دقیقهٔ ۸ تا ۹ — دیتابیس. اتصال را چک کنید و اگر یک صفحه خاص می‌شکند، همان جدول را بررسی کنید. موتور جدول را ببینید، نه فقط پیام را.
  8. دقیقهٔ ۹ تا ۱۰ — تصمیم. اگر ریشه پیدا شد، درمان و بعد تأیید. اگر پیدا نشد، بکاپ را بازگردانید تا سایت بالا بیاید و همان پنج قلم را به هاست بدهید.

دو قاعدهٔ حاکم بر کل این ده دقیقه. اول: اگر دو تغییر پشت سر هم جواب نداد، هر دو را برگردانید؛ وگرنه وضعیتی دارید که دیگر نمی‌شناسیدش و نمی‌توانید بگویید کدام تغییر چه کرد. دوم: چیزی را که نمی‌فهمید پاک نکنید — حذف یک جدول یا یک پوشه، از ۵۰۰ به حادثهٔ داده می‌رسد و آن یکی برگشت‌پذیر نیست.

پرسش‌های متداول خطای ۵۰۰

آیا خطای ۵۰۰ همیشه تقصیر افزونه است؟

نه. افزونه و قالب شایع‌ترین‌اند، ولی سه رقیب جدی دارند: یک دستور نامعتبر در .htaccess یا php_value زیر PHP-FPM، اتمام حافظه یا زمان اجرا، و خطای دیتابیس. اول از لاگ بخوانید کدام است؛ اسم فایل در لاگ، جواب است.

تفاوت ۵۰۰ با پیام «There has been a critical error on this website» چیست؟

آن پیام از خود وردپرس است و یعنی وردپرس بارگذاری شده و بعد خطای مهلک خورده؛ معمولاً لاگ و ایمیل مدیر هم دارد و افزونهٔ مقصر در دسترس است. ۵۰۰ خالی و بی‌پیام بیشتر وقت‌ها یعنی PHP پیش از رسیدن به وردپرس مرده یا وب‌سرور درخواست را رد کرده است.

آیا روشن کردن WP_DEBUG روی سایت زنده امن است؟

فقط برای چند دقیقه و فقط با WP_DEBUG_DISPLAY برابر false. در این حالت خطاها به wp-content/debug.log می‌روند و به بازدیدکننده نشان داده نمی‌شوند. خود این فایل به‌طور پیش‌فرض از بیرون قابل خواندن است، پس دسترسی‌اش را ببندید و بعد از تشخیص پاکش کنید.

بعد از رفع خطا، چطور مطمئن شوم برنگشته؟

سه چیز را چک کنید: صفحهٔ خطادار و پیشخوان را بزنید، لاگ را برای همان الگو دوباره بگردید، و بیست‌وچهار ساعت لاگ را ببینید. اگر خطا با فاصلهٔ منظم برمی‌گردد — مثلاً هر شب سر ساعت پشتیبان‌گیری — مسئله منابع است، نه کد.

اگر به پیشخوان و فایل هم دسترسی ندارم چه کار کنم؟

دسترسی فایل را از پنل هاست یا FTP بگیرید؛ با همان می‌توانید افزونه‌ها را خاموش کنید و wp-config.php را تغییر دهید. اگر آن هم نبود، تیکت هاست با پنج قلم بدهید: زمان با منطقهٔ زمانی، آدرس، کد وضعیت، آخرین تغییر، و یک ردیف لاگ. اگر سایت فروش دارد، در همان تیکت بازگردانی بکاپ را هم بخواهید.

نوشته‌شده و بازبینی‌شده توسط

متخصص سئو، وردپرس و تولید محتوا

اولین نفری باش که نظر می‌ده!

فقط نام لازم است، ایمیل اختیاریه و منتشر نمی‌شه.