عیب‌یابی

رفع خطای 500 Internal Server Error — عیب‌یابی قدم‌به‌قدم وردپرس و سرور

صفحه‌ی خطای 500 Internal Server Error روی نمایشگر و مسیر عیب‌یابی آن در لاگ‌های سرور

خطای 500 Internal Server Error مبهم‌ترین پیغام دنیای وب است: سرور فقط می‌گوید «یک جای کار خراب شد» و حتی یک کلمه توضیح نمی‌دهد کجا. خبر خوب این‌که ابهام فقط روی صفحه است — دلیل واقعی همیشه در یکی از لاگ‌های سرور ثبت شده و رفع ارور 500 از لحظه‌ای شروع می‌شود که همان لاگ را باز کنید. در این راهنما همین مسیر را قدم‌به‌قدم می‌رویم: هر لاگ کجاست، شایع‌ترین علت‌ها به ترتیب فراوانی کدام‌اند و چطور افزونه‌ی خراب وردپرس را بدون نیاز به wp-admin غیرفعال کنید.

خطای ۵۰۰ دقیقاً یعنی چه؟

کد وضعیت HTTP 500 یک «کد همه‌منظوره» برای خطاهای سمت سرور است: درخواست شما سالم رسیده، اپلیکیشن شروع به ساختن پاسخ کرده و وسط کار زمین خورده — یک fatal error در PHP، یک دستور خراب در .htaccess، یا فایلی که اجازه‌ی اجرایش نیست. برخلاف اسمش، مشکل تقریباً هیچ‌وقت از خودِ سرور (سخت‌افزار و شبکه) نیست؛ از کد و پیکربندی‌ای است که روی آن اجرا می‌شود.

همین تعریف، مرز ۵۰۰ با هم‌خانواده‌هایش را روشن می‌کند: در ۵۰۲ درخواست اصلاً به اپلیکیشن نمی‌رسد چون بک‌اند مرده یا پراکسی آدرسش را اشتباه دارد؛ در ۵۰۰ اپلیکیشن زنده است و درخواست را هم گرفته — فقط از پسِ ساختن جواب برنیامده:

کدچه اتفاقی افتادهمقصر معمولاولین قدم
500 Internal Server Errorاپلیکیشن هنگام ساخت پاسخ کرش کردافزونه یا کد PHP، .htaccess، پرمیشن، دیسک پرخواندن لاگ خطا (همین مقاله)
502 Bad Gatewayپراکسی از بک‌اند جواب سالم نگرفتPHP-FPM خوابیده، سوکت اشتباه، کمبود رمsystemctl status php8.3-fpm
503 Service Unavailableسرویس عمداً یا از فشار «فعلاً در دسترس نیست»حالت تعمیر وردپرس، اشباع منابعحذف فایل .maintenance، بررسی بار
504 Gateway Timeoutبک‌اند زنده است ولی دیر جواب دادکوئری سنگین، تایم‌اوت کوتاهبهینه‌سازی کد یا افزایش هدفمند مهلت

اگر پیغام شما 502 است، مسیر عیب‌یابی متفاوتی دارد که در راهنمای رفع خطای 502 Bad Gateway جداگانه و کامل رفته‌ایم. این مقاله روی ۵۰۰ تمرکز می‌کند: «کد یا کانفیگ خودم خراب است.»

قانون طلایی: دلیل خطا روی صفحه نیست، در لاگ است

صفحه‌ی سفید ۵۰۰ عمداً چیزی نمی‌گوید؛ نمایش جزئیات خطا به بازدیدکننده یعنی لو دادن مسیر فایل‌ها و ساختار کد به همه، از جمله مهاجم‌ها. قانون طلایی این است: هر رفع ارور 500 با باز کردن لاگ شروع می‌شود، نه با حدس‌زدن و ری‌استارت کورکورانه. بسته به پشته‌تان، لاگ این‌جاست:

# Nginx
sudo tail -f /var/log/nginx/error.log
# Apache on Debian/Ubuntu
sudo tail -f /var/log/apache2/error.log
# Apache on AlmaLinux/Rocky
sudo tail -f /var/log/httpd/error_log
# cPanel (Apache or LiteSpeed)
sudo tail -f /usr/local/apache/logs/error_log
# PHP-FPM pool log on AlmaLinux/Rocky
sudo tail -f /var/log/php-fpm/www-error.log
# WordPress debug log (only exists after enabling WP_DEBUG_LOG)
tail -f /var/www/example.com/wp-content/debug.log

نکته‌ی اوبونتو: اگر برای pool لاگ جداگانه‌ای تنظیم نشده باشد، فتال‌ارورهای PHP با پیشوند FastCGI sent in stderr داخل همان error.log انجین‌ایکس ظاهر می‌شوند:

2026/08/06 10:41:23 [error] 1812#1812: *44 FastCGI sent in stderr:
"PHP message: PHP Fatal error:  Uncaught Error: Call to undefined function
old_slider_init() in /var/www/example.com/wp-content/plugins/old-slider/old-slider.php:87"
while reading response header from upstream, client: 203.0.113.7

روش درست کار، بازتولید زیر نظر لاگ است: در یک ترمینال tail -f را باز نگه دارید و در ترمینال دوم (یا مرورگر) صفحه‌ی خراب را صدا بزنید؛ خطی که دقیقاً همان لحظه اضافه می‌شود، پرونده‌ی شماست. برای گرفتن کد واقعی هم به مرورگر اعتماد نکنید — مرورگر و CDN گاهی نسخه‌ی کش‌شده‌ی سالم را نشان می‌دهند در حالی که بقیه ۵۰۰ می‌گیرند:

curl -s -o /dev/null -w "%{http_code}\n" https://example.com/
# 500
curl -sI https://example.com/ | head -n 5
# check x-cache / cf-cache-status headers to spot cached replies

اگر با tail و grep و خواندن لاگ راحت نیستید، یک مرور سریع دستورات پرکاربرد لینوکس دقیقاً همین ابزارها را دستتان می‌دهد.

💡 نکته: آخرین خط لاگ لزوماً مال خطای شما نیست؛ شاید مال ربات یا بازدیدکننده‌ی دیگری باشد. همیشه زمان خط لاگ را با لحظه‌ی بازتولید خودتان تطبیق بدهید — برای همین است که tail -f زنده بهتر از باز کردن فایل بعد از ماجراست.
فلوچارت عیب‌یابی خطای 500 — خواندن لاگ سرور و سه شاخه‌ی خطای PHP، مجوز فایل‌ها و کمبود حافظه یا دیسک با راه‌حل هر شاخه

علت‌های رایج خطای 500 — به ترتیب فراوانی

از قدم اول شروع کنید و پایین نروید مگر این‌که لاگ چیز دیگری بگوید.

۱. فتال‌ارور PHP بعد از به‌روزرسانی افزونه یا قالب

پرتکرارترین سناریو در وردپرس: دیشب افزونه‌ای آپدیت شده و امروز کل سایت — حتی wp-admin — سفید است. لاگ معمولاً مستقیم اسم مقصر را می‌گوید، چون مسیر فایل خطا داخل پوشه‌ی همان افزونه است. درمان به wp-admin نیاز ندارد؛ از SSH پوشه‌ی افزونه را تغییر نام بدهید تا وردپرس آن را بارگذاری نکند:

cd /var/www/example.com/wp-content/plugins
mv old-slider old-slider.off
# not sure which plugin? disable them all at once:
mv /var/www/example.com/wp-content/plugins /var/www/example.com/wp-content/plugins.off

اگر مقصر قالب است، همین کار را با پوشه‌ی قالب در wp-content/themes بکنید تا وردپرس به قالب پیش‌فرض برگردد (اگر یکی از قالب‌های Twenty نصب باشد). بعد از بالا آمدن سایت، پوشه را برگردانید و افزونه‌ها را یکی‌یکی فعال کنید تا خرابکار پیدا شود. اگر وردپرس را خودتان روی سرور اداره می‌کنید، راهنمای نصب و انتقال وردپرس روی سرور مجازی ساختار درست همین مسیرها را نشان می‌دهد.

۲. فایل .htaccess خراب یا بیش‌ازحد سخت‌گیر

روی آپاچی و لایت‌اسپید، یک غلط تایپی در .htaccess یا قانونی که افزونه‌ی امنیتی اضافه کرده برای ۵۰۰ دائمی کافی است؛ لاگ آپاچی در این حالت چیزی شبیه Invalid command 'RewriteRul', perhaps misspelled می‌نویسد. تست یک‌خطی دارد:

cd /var/www/example.com
mv .htaccess .htaccess.broken
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/

اگر کد از 500 به 200 (یا 301) برگشت، مقصر پیدا شده. برای وردپرس ساده‌ترین درمان، ساخت دوباره‌ی فایل استاندارد است — یا از پیشخوان (تنظیمات ← پیوندهای یکتا ← ذخیره) یا دستی:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

Nginx اصلاً .htaccess را نمی‌خواند؛ اگر پشته‌تان Nginx است این بخش را رد کنید — جای درست این قواعد را راهنمای راه‌اندازی سایت با Nginx نشان می‌دهد.

۳. اتمام memory_limit

امضایش در لاگ بی‌همتاست:

PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted
(tried to allocate 2621440 bytes) in /var/www/example.com/wp-includes/class-wp-image-editor-gd.php

عدد ۱۳۴۲۱۷۷۲۸ همان ۱۲۸ مگابایت است — سقف حافظه‌ی هر درخواست، نه کل سرور. معمولاً یک عملیات سنگین (ویرایش تصویر بزرگ، ایمپورت، گزارش ووکامرس) آن را می‌ترکاند. سقف را آگاهانه و پله‌پله بالا ببرید:

# replace 8.3 with your PHP version (php -v)
grep "^memory_limit" /etc/php/8.3/fpm/php.ini
# memory_limit = 128M
sudo sed -i 's/^memory_limit.*/memory_limit = 256M/' /etc/php/8.3/fpm/php.ini
sudo systemctl reload php8.3-fpm

«آگاهانه» یعنی کورکورانه ۲ گیگابایت نگذارید: اگر صفحه‌ای با ۵۱۲ مگابایت هم می‌میرد، با نشتی حافظه یا حلقه‌ی معیوب طرفید و سقف بالاتر فقط مرگ را عقب می‌اندازد — ضمن این‌که حاصل‌ضرب این عدد در تعداد workerها باید در رم سرور جا شود.

۴. ناسازگاری نسخه‌ی PHP بعد از ارتقا

سرور به PHP 8.3 ارتقا یافته و افزونه‌ای قدیمی هنوز create_function() یا each() صدا می‌زند — توابعی که در PHP 8 حذف شده‌اند. نتیجه یک فتال‌ارور و ۵۰۰ روی هر صفحه‌ای است که آن کد را لمس کند. لاگ باز هم مسیر فایل را می‌گوید؛ راه‌حل پایدار به‌روزرسانی یا تعویض همان افزونه است و راه‌حل موقت، برگرداندن همان سایت (نه کل سرور) به نسخه‌ی قبلی PHP تا فرصت اصلاح داشته باشید.

۵. پرمیشن و مالکیت به‌هم‌ریخته

یک chown یا chmod -R عجولانه — یا آپلود فایل‌ها با کاربر root — کافی است تا PHP دیگر نتواند فایل‌ها را بخواند یا در آن‌ها بنویسد. روی بعضی پیکربندی‌ها (suPHP و suexec در cPanel) حتی پرمیشن ۷۷۷ عمداً با ۵۰۰ جواب می‌گیرد، چون ناامن تلقی می‌شود. مقادیر امن و استاندارد: فایل 644، پوشه 755، مالک همان کاربری که pool با آن اجرا می‌شود:

grep "^user" /etc/php/8.3/fpm/pool.d/www.conf
# user = www-data
sudo chown -R www-data:www-data /var/www/example.com
sudo find /var/www/example.com -type d -exec chmod 755 {} \;
sudo find /var/www/example.com -type f -exec chmod 644 {} \;

۶. دیسک پر

وقتی جایی برای نوشتن نیست، سشن‌های PHP ساخته نمی‌شوند، کش و فایل‌های موقت شکست می‌خورند و اپلیکیشنی که به این نوشتن‌ها تکیه دارد با ۵۰۰ می‌خوابد — و بدتر این‌که خودِ لاگ هم دیگر نوشته نمی‌شود:

df -h
df -i
# find what is eating the disk
sudo du -xh /var --max-depth=2 | sort -rh | head -n 10

df -i را جدی بگیرید: دیسکی که «جا دارد» ولی inodeهایش (مثلاً با میلیون‌ها فایل سشن کوچک) تمام شده، دقیقاً همین علائم را می‌دهد. برای این‌که دفعه‌ی بعد قبل از پر شدن باخبر شوید، هشدار خودکار مصرف دیسک را از راهنمای مانیتورینگ سرور لینوکس راه بیندازید.

۷. OPcache خراب بعد از دیپلوی

OPcache بایت‌کد PHP را در حافظه نگه می‌دارد تا هر درخواست دوباره کامپایل نشود. اگر بلافاصله بعد از دیپلوی یا آپدیت، خطاهای عجیبی مثل Cannot redeclare class یا صدا زدن متدی که «قطعاً وجود دارد» می‌بینید، احتمالاً ترکیبی از بایت‌کد قدیمی و فایل‌های جدید در حال اجراست — مخصوصاً وقتی opcache.validate_timestamps=0 تنظیم شده باشد. درمان یک خط است و امن‌ترین شکلش reload سرویس است:

sudo systemctl reload php8.3-fpm

اگر از این تنظیم برای سرعت استفاده می‌کنید، reload را جزو ثابت اسکریپت دیپلوی کنید. نقش OPcache در کارایی را در راهنمای افزایش سرعت وردپرس کامل باز کرده‌ایم.

WP_DEBUG را امن روشن کنید

وقتی لاگ سرور به هر دلیل در دسترس نیست (مثلاً روی هاست اشتراکی)، وردپرس می‌تواند لاگ خودش را بنویسد. در wp-config.php، بالای خط /* That's all, stop editing! */ این سه خط را بگذارید:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );

از این لحظه همه‌ی خطاها و اخطارها در wp-content/debug.log جمع می‌شوند. یک بار خطا را بازتولید کنید، فایل را بخوانید و بعد از پایان عیب‌یابی هر سه را به حالت قبل برگردانید — لاگ debug روی سایت پربازدید سریع بزرگ می‌شود.

⚠️ WP_DEBUG_DISPLAY را روی سایت اصلی هرگز true نکنید: جزئیات خطا — مسیر کامل فایل‌ها و گاهی تکه‌های کوئری — جلوی چشم همه‌ی بازدیدکننده‌ها و خزنده‌ها می‌افتد. به همین دلیل display_errors = Off در PHP سایت اصلی یک تنظیم درست است، نه مشکلی که باید رفعش کنید؛ خطا باید در لاگ ثبت شود (log_errors = On)، نه روی صفحه.

وقتی خطا گاه‌به‌گاه می‌آید و می‌رود

۵۰۰ دائمی را لاگ در چند دقیقه حل می‌کند؛ ۵۰۰ «هر از گاهی» موذی‌تر است — تا شما نگاه می‌کنید، همه‌چیز سالم است. دو الگوی کلاسیک دارد.

الگوی اول: OOM Killer. رم که تمام شود، کرنل برای زنده ماندن یکی از پروسه‌های پرمصرف — اغلب یکی از فرزندهای PHP-FPM یا MySQL — را می‌کُشد. درخواست‌هایی که همان لحظه وسط اجرا بودند با خطای ۵xx می‌میرند، systemd سرویس را دوباره بالا می‌آورد و سایت «خودش خوب می‌شود» — تا قحطی بعدی. مدرک را از کرنل بگیرید:

sudo dmesg -T | grep -i oom
sudo journalctl -k | grep -i "out of memory"
free -h

الگوی دوم: قله‌های ساعت کرون. اگر خطاها الگوی زمانی دارند — دقیقاً سرِ ساعت، یا هر شب حوالی ۳ بامداد — زمان خطاهای لاگ را با زمان‌بندی کرون‌جاب‌ها تطبیق بدهید؛ بکاپ فشرده، ایندکس‌گیری و wp-cron روی سایت شلوغ می‌توانند چند دقیقه CPU و رم را قبضه کنند:

crontab -l
ls /etc/cron.d/
# Debian/Ubuntu
grep CRON /var/log/syslog | tail -n 20
# AlmaLinux/Rocky
sudo tail -n 20 /var/log/cron

درمان، پخش کردن کارهای سنگین در ساعت‌های مختلف و اجرای بکاپ با nice و ionice است — و اگر رم واقعاً کم است، ارتقای منابع؛ روی سرور ابری این ارتقا چند کلیک بیشتر نیست.

چک‌لیست ۱۰ دقیقه‌ای رفع ارور 500

وقتی سایت پایین است، به حافظه اعتماد نکنید؛ این ده قدم را به ترتیب بروید:

  1. کد واقعی را بگیرید: curl -s -o /dev/null -w "%{http_code}\n" https://example.com/ — شاید اصلاً ۵۰۲ یا ۵۰۳ باشد و مسیرتان عوض شود.
  2. لاگ را زنده کنید: tail -f روی لاگ خطای وب‌سرور، و همزمان صفحه را یک بار باز کنید.
  3. خط خطا را بخوانید: عبارت Fatal error و مسیر فایل، معمولاً پرونده را همین‌جا می‌بندد.
  4. بپرسید «چه چیزی عوض شده؟»: آپدیت افزونه، دیپلوی، ارتقای PHP — آخرین تغییر، مظنون اول است.
  5. افزونه‌ی مشکوک را از SSH خاموش کنید: تغییر نام پوشه‌اش در wp-content/plugins کافی است.
  6. روی آپاچی/لایت‌اسپید .htaccess را موقتاً کنار بگذارید و دوباره با curl تست بگیرید.
  7. منابع را چک کنید: df -h و df -i برای دیسک، free -h برای رم.
  8. ردپای OOM را بگیرید: sudo dmesg -T | grep -i oom — مخصوصاً اگر خطا دوره‌ای است.
  9. مالکیت و پرمیشن را برگردانید: فایل 644، پوشه 755، مالک برابر کاربر pool.
  10. هنوز گیر است؟ systemctl reload php8.3-fpm برای OPcache تازه، بعد WP_DEBUG_LOG را روشن کنید و یک بار بازتولید کنید.
💡 نکته: در هر قدم فقط یک چیز را تغییر بدهید و بلافاصله با همان دستور curl تست بگیرید. اگر سه چیز را با هم عوض کنید و سایت برگردد، نمی‌فهمید کدام درمان بود — و دفعه‌ی بعد باید از صفر شروع کنید.

سؤالات پرتکرار

خطای 500 یعنی چه و مقصرش کیست؟

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

فرق خطای 500 با 502 چیست؟

در ۵۰۰ اپلیکیشن زنده است و وسط اجرای کد زمین می‌خورد؛ در ۵۰۲ پراکسی (مثل Nginx) اصلاً نمی‌تواند از بک‌اند جواب سالم بگیرد چون سرویس پشتی مرده یا آدرسش اشتباه است. برای همین ۵۰۰ را با خواندن لاگ و اصلاح کد و کانفیگ درمان می‌کنید و ۵۰۲ را با زنده کردن بک‌اند.

خطای 500 وردپرس بعد از نصب افزونه را چطور بدون wp-admin رفع کنم؟

با SSH وارد سرور شوید و در مسیر wp-content/plugins نام پوشه‌ی همان افزونه را تغییر بدهید (مثلاً به plugin.off)؛ وردپرس افزونه‌ای را که پوشه‌اش نیست بارگذاری نمی‌کند و سایت بلافاصله برمی‌گردد. اگر نمی‌دانید کدام افزونه مقصر است، کل پوشه‌ی plugins را موقتاً تغییر نام بدهید و بعد یکی‌یکی فعال کنید.

چرا خطای 500 گاهی می‌آید و خودش می‌رود؟

خطای متناوب معمولاً یعنی کمبود منابع، نه باگ ثابت: یا OOM Killer در لحظه‌های پرفشار پروسه‌ای را می‌کُشد، یا یک کرون‌جاب سنگین در ساعت مشخصی منابع را قبضه می‌کند، یا فقط صفحه‌های خاص و سنگین به سقف memory_limit می‌خورند. زمان دقیق خطاها در لاگ، الگو را لو می‌دهد.

قدم بعدی

از این به بعد صفحه‌ی سفید ۵۰۰ ترسناک نیست: لاگ را باز می‌کنید، خط Fatal error را پیدا می‌کنید و در چند دقیقه به فایل مقصر می‌رسید. اگر می‌خواهید این مهارت در دستتان بنشیند، روی سروری تمرین کنید که خراب شدنش مهم نباشد: یک سرور ابری ساعتی بسازید — حدود ۶۰ ثانیه بعد سرور KVM با دیسک NVMe آماده است — عمداً افزونه‌ی ناسازگار نصب کنید، .htaccess را به هم بریزید، دیسک را پر کنید و هر بار از روی لاگ به ریشه برسید؛ هزینه فقط همان چند ساعت تمرین است.

آموزش‌های مرتبط

آماده‌ی تمرین عملی هستید؟

سرور ابری ساعتی مهران هاست در ۶۰ ثانیه تحویل می‌شود — تمرین کنید و فقط بابت همان ساعت‌ها پرداخت کنید. هزینه را پیش از ثبت‌نام با محاسبه‌گر برآورد کنید.