عیب‌یابی

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

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

رفع خطای 500 با یک واقعیت ساده شروع می‌شود: پیغام Internal Server Error مبهم‌ترین پیغام دنیای وب است — سرور فقط می‌گوید «یک جای کار خراب شد» و حتی یک کلمه توضیح نمی‌دهد کجا. خبر خوب این‌که ابهام فقط روی صفحه است — دلیل واقعی همیشه در یکی از لاگ‌های سرور ثبت شده و رفع ارور 500 از لحظه‌ای شروع می‌شود که همان لاگ را باز کنید. در این راهنما همین مسیر را قدم‌به‌قدم می‌رویم: هر لاگ کجاست، شایع‌ترین علت‌ها به ترتیب فراوانی کدام‌اند، روی هاست اشتراکی بدون SSH چه می‌شود کرد و چطور افزونه‌ی خراب وردپرس را بدون نیاز به 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
# DirectAdmin (one file per domain)
sudo tail -f /var/log/httpd/domains/example.com.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 نصب باشد). بعد از بالا آمدن سایت، پوشه را برگردانید و افزونه‌ها را یکی‌یکی فعال کنید تا خرابکار پیدا شود. اگر وردپرس را خودتان روی سرور اداره می‌کنید، راهنمای نصب و انتقال وردپرس روی سرور مجازی ساختار درست همین مسیرها را نشان می‌دهد.

یک میان‌بر هم هست: وردپرس از نسخه‌ی ۵.۲ «حالت بازیابی» (Recovery Mode) دارد. اگر فتال‌ارور از نوعی باشد که خودش بگیرد، به‌جای صفحه‌ی سفید پیغام «There has been a critical error on this website» را نشان می‌دهد و به ایمیل مدیر سایت لینکی می‌فرستد که با آن وارد پیشخوان می‌شوید در حالی که افزونه یا قالب خراب فقط برای همان نشست شما متوقف شده است. سه کاوِت واقعی دارد. اول، این ایمیل از خودِ سرور ارسال می‌شود و اگر ایمیل سرور اسپم تلقی شود هرگز نمی‌رسد — و وردپرس آن را حداکثر روزی یک بار می‌فرستد (زمان آخرین ارسال در گزینه‌ی recovery_mode_email_last_sent ذخیره می‌شود)، پس اگر بار اول به اسپم رفت، تا ۲۴ ساعت خبری از ایمیل دوم نیست؛ گیرنده را می‌توان با ثابت RECOVERY_MODE_EMAIL در wp-config.php به آدرسی مطمئن‌تر از ایمیل مدیر عوض کرد. دوم، ایمیل فقط وقتی ارسال می‌شود که خطا در wp-admin یا wp-login.php رخ بدهد، نه در اجرای wp-cron و کارهای پس‌زمینه و نه در بارگذاری صرفِ صفحه‌های عمومی — خطایی که فقط در فرانت‌اند می‌افتد صفحه‌ی critical error را نشان می‌دهد ولی ایمیلی نمی‌فرستد؛ پس برای گرفتن لینک بازیابی یک بار /wp-admin/ را باز کنید. سوم، فقط خطاهایی را می‌گیرد که بعد از بارگذاری خودِ هندلر رخ بدهند: خطای نحوی در wp-config.php، خرابی .htaccess و اتمام حافظه‌ای که پروسه را قبل از اجرای شات‌داون وردپرس بکُشد، از دستش درمی‌روند — این‌ها همان ۵۰۰ سفید کلاسیک‌اند و مسیرشان همین لاگ است. (خطای پارس داخل یک افزونه اما گرفته می‌شود؛ هندلر وردپرس E_PARSE را هم کنار E_ERROR پوشش می‌دهد.)

۲. فایل .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

در وردپرس یک لایه‌ی دوم هم هست که خیلی‌ها را گیج می‌کند: خودِ وردپرس در شروع هر درخواست سقف را به WP_MEMORY_LIMIT (پیش‌فرض ۴۰ مگابایت، در مولتی‌سایت ۶۴) و در پیشخوان به WP_MAX_MEMORY_LIMIT (پیش‌فرض ۲۵۶ مگابایت) بالا می‌برد — هرگز پایین نمی‌آورد — و فقط وقتی این کار از دستش برمی‌آید که هاست اجازه‌ی تغییر memory_limit در زمان اجرا را داده باشد. پس اگر روی هاست اشتراکی به php.ini دسترسی ندارید، این خط را در wp-config.php امتحان کنید و اگر عدد داخل خطای لاگ عوض نشد، یعنی سقف را باید از ویرایشگر php.ini پنل هاست بالا ببرید:

define( 'WP_MEMORY_LIMIT', '256M' );

«آگاهانه» یعنی کورکورانه ۲ گیگابایت نگذارید: اگر صفحه‌ای با ۵۱۲ مگابایت هم می‌میرد، با نشتی حافظه یا حلقه‌ی معیوب طرفید و سقف بالاتر فقط مرگ را عقب می‌اندازد — ضمن این‌که حاصل‌ضرب این عدد در تعداد workerها باید در رم سرور جا شود: با pm.max_children = 20 و سقف ۲۵۶ مگابایت، بدترین حالت ۵ گیگابایت است — روی سروری با ۴ گیگابایت رم، همین «رفع خطا» چند ساعت بعد به OOM Killer ختم می‌شود.

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

سرور به PHP 8.3 ارتقا یافته و افزونه‌ای قدیمی هنوز create_function() یا each() صدا می‌زند — توابعی که در PHP 8 حذف شده‌اند. نتیجه یک فتال‌ارور و ۵۰۰ روی هر صفحه‌ای است که آن کد را لمس کند. این سناریو بعد از مهاجرت به توزیع جدید زیاد پیش می‌آید: Ubuntu 24.04 به‌طور پیش‌فرض PHP 8.3 نصب می‌کند و AlmaLinux 9 با PHP 8.0 می‌آید و نسخه‌های جدیدتر را از module stream یا مخزن Remi می‌گیرد؛ کدی که روی سرور قبلی با PHP 7.4 زنده بود، این‌جا در اولین درخواست می‌میرد. لاگ باز هم مسیر فایل را می‌گوید؛ راه‌حل پایدار به‌روزرسانی یا تعویض همان افزونه است و راه‌حل موقت، برگرداندن همان سایت (نه کل سرور) به نسخه‌ی قبلی 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 در کارایی را در راهنمای افزایش سرعت وردپرس کامل باز کرده‌ایم.

وقتی خودِ وب‌سرور ۵۰۰ می‌دهد: حلقه‌ی ریدایرکت داخلی

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

# Apache: a RewriteRule that keeps matching its own target
[core:error] [pid 1912] [client 203.0.113.7:51234] AH00124: Request exceeded the limit
of 10 internal redirects due to probable configuration error. Use 'LimitInternalRecursion'
to increase the limit if necessary. Use 'LogLevel debug' to get a backtrace.

# Nginx: try_files that falls back to a URI handled by the same location
2026/09/14 22:10:05 [error] 2231#2231: *17 rewrite or internal redirection cycle
while internally redirecting to "/index.php", client: 203.0.113.7, server: example.com,
request: "GET / HTTP/2.0", host: "example.com"

در آپاچی مقصر معمولاً یک RewriteRule بدون شرط‌های !-f و !-d است، یا صفحه‌ی خطای سفارشی‌ای که خودش زیر همان قانون می‌افتد؛ سقف ۱۰ دور را با LimitInternalRecursion بالا نبرید — حلقه را قطع کنید. در Nginx شایع‌ترین شکلش این است که مقصد try_files (مثلاً /index.php) در مسیر root یا alias وجود ندارد — root اشتباه یا فایل جابه‌جاشده — پس ریدایرکت داخلی دوباره به همان location / می‌افتد و باز به همان مقصد ناموجود می‌رسد. (اگر فقط بلاک location ~ \.php$ نباشد، نتیجه ۵۰۰ نیست؛ Nginx فایل index.php را به‌صورت استاتیک تحویل می‌دهد و سورس PHP لو می‌رود.) تست پیکربندی قبل از هر reload واجب است:

sudo nginx -t
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
sudo apachectl configtest
# Syntax OK
⚠️ nginx -t و configtest فقط نحو را چک می‌کنند، نه منطق را. حلقه‌ی ریدایرکت از هر دو با «Syntax OK» رد می‌شود و تازه سرِ اولین درخواست خودش را نشان می‌دهد؛ برای همین بعد از هر تغییر قانون بازنویسی، یک بار با curl از خودِ سرور تست بگیرید.

چرا فقط wp-admin خطای ۵۰۰ می‌دهد و سایت سالم است؟

این الگو آن‌قدر رایج است که خودش یک جست‌وجوی جداگانه است: صفحه‌ی اصلی بالا می‌آید ولی ورود به پیشخوان ۵۰۰ می‌دهد. سه دلیل بیشتر ندارد و لاگ بین‌شان داوری می‌کند.

اول، حافظه. پیشخوان کد بسیار بیشتری بارگذاری می‌کند — داشبورد افزونه‌ها، گزارش‌های ووکامرس، بازسازی بندانگشتی‌ها با GD — و همان‌طور که بالاتر دیدید، وردپرس برای پیشخوان سعی می‌کند سقف را تا ۲۵۶ مگابایت بالا ببرد. اگر هاست اجازه‌ی تغییر memory_limit در زمان اجرا را نداده باشد، پیشخوان با همان ۱۲۸ مگابایت جلو (یا کمتر) می‌ماند و اولین کار سنگین آن را می‌ترکاند؛ خط Allowed memory size در لاگ همین را می‌گوید و درمانش بالا بردن سقف از php.ini است، نه از wp-config.

دوم، کدی که فقط در پیشخوان اجرا می‌شود. بسیاری از افزونه‌ها بخش مدیریتی‌شان را روی هوک admin_init یا admin_menu بار می‌کنند؛ اگر همان بخش با نسخه‌ی جدید PHP یا وردپرس ناسازگار باشد، سایت سالم می‌ماند و پیشخوان می‌میرد. مسیر فایل داخل لاگ مستقیم به پوشه‌ی افزونه اشاره می‌کند و همان تغییر نام پوشه‌ی افزونه کار را تمام می‌کند.

سوم، یک .htaccess دوم داخل wp-admin. افزونه‌های امنیتی و بعضی راهنماها برای محافظت از پیشخوان، یک فایل .htaccess با رمز جداگانه (Basic Auth) داخل این پوشه می‌سازند. اگر فایل رمز جابه‌جا شود یا سایت به سرور دیگری منتقل شود و مسیر AuthUserFile دیگر وجود نداشته باشد، آپاچی به‌جای پنجره‌ی رمز، ۵۰۰ می‌دهد و در لاگ می‌نویسد AH01620: Could not open password file. تست همان یک خط همیشگی است:

ls -la /var/www/example.com/wp-admin/.htaccess
mv /var/www/example.com/wp-admin/.htaccess /var/www/example.com/wp-admin/.htaccess.off
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/wp-admin/
# 302 (redirect to wp-login.php) means the admin is alive again

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 روی سایت پربازدید سریع بزرگ می‌شود. اگر می‌خواهید فایل لاگ از دسترس عمومی دور بماند، به‌جای true می‌توانید به WP_DEBUG_LOG یک مسیر کامل خارج از پوشه‌ی عمومی سایت بدهید — مستندات رسمی وردپرس هم مسیر خارج از وب‌روت (مثل /tmp/wp-errors.log) را به‌عنوان نمونه می‌آورد. دلیل ما برای این توصیه امنیتی است: debug.log داخل wp-content با یک آدرس ساده از مرورگر قابل خواندن است.

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

رفع خطای 500 روی هاست اشتراکی بدون SSH

روی هاست اشتراکی نه tail -f دارید و نه php.ini سرور، ولی همان منطق با ابزارهای کنترل‌پنل هاست (cPanel یا DirectAdmin) قابل اجراست. روی هاست اشتراکی مهران هاست این کارها را از داخل پنل فارسی انجام می‌دهید، نه از خودِ cPanel/DirectAdmin: تغییر نام پوشه‌ی افزونه، کنار گذاشتن موقت .htaccess و برگرداندن پرمیشن 644/755 از تب «فایل‌ها» (مدیر فایل پنل rename و chmod دارد) شدنی است؛ خواندن لاگ خطا و تغییر نسخه‌ی PHP یا memory_limit را با یک تیکت از پشتیبانی بخواهید. اگر روی هاست دیگری هستید و به خودِ cPanel یا DirectAdmin دسترسی دارید، مسیر به این ترتیب است:

  1. لاگ را از پنل بخوانید. در cPanel صفحه‌ی «Errors» (زیر بخش Metrics) تا ۳۰۰ خط آخر لاگ خطای وب‌سرور را از جدید به قدیم نشان می‌دهد؛ برای بیشتر از آن باید فایل لاگ را دانلود کنید. در DirectAdmin از «Site Summary / Statistics / Logs» گزینه‌ی Error Log هر دامنه را باز کنید — همان فایل /var/log/httpd/domains/example.com.error.log است که مدیر سرور می‌بیند. خط Fatal error با مسیر فایل، این‌جا هم پرونده را می‌بندد.
  2. افزونه را از مدیر فایل خاموش کنید. در File Manager به wp-content/plugins بروید و پوشه‌ی افزونه‌ی مقصر را تغییر نام بدهید؛ دقیقاً همان کاری که در SSH با mv می‌کردید. همین ابزار برای کنار گذاشتن موقت .htaccess هم کافی است.
  3. نسخه‌ی PHP و memory_limit را از پنل تنظیم کنید. هر دو پنل صفحه‌ای برای انتخاب نسخه‌ی PHP هر دامنه و ویرایش مقادیر php.ini دارند؛ پایین آوردن موقت نسخه (مثلاً از ۸.۳ به ۸.۱) بعد از ۵۰۰ ناشی از افزونه‌ی قدیمی، و بالا بردن memory_limit بعد از خط Allowed memory size، همین‌جا انجام می‌شود.
  4. پرمیشن ۷۷۷ را برگردانید. روی cPanel با suPHP یا suexec، فایل یا پوشه‌ی ۷۷۷ عمداً ۵۰۰ می‌گیرد؛ در مدیر فایل مقدار را به 644 برای فایل و 755 برای پوشه برگردانید.
💡 نکته: روی هاست اشتراکی دیسک پر معمولاً «سهمیه‌ی پر» است، نه دیسک فیزیکی: سهمیه‌ی فضای حساب که تمام شود، PHP نمی‌تواند سشن و فایل موقت بنویسد و علائمش عین دیسک پر روی سرور است. نوار مصرف فضا در صفحه‌ی اول پنل را قبل از هر کار دیگری نگاه کنید.

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

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

الگوی اول: 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 (service is named "cron")
sudo journalctl -u cron --since "3 hours ago" --no-pager
# AlmaLinux/Rocky (service is named "crond")
sudo journalctl -u crond --since "3 hours ago" --no-pager
sudo tail -n 20 /var/log/cron

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

خطای 500 با سئو چه می‌کند و بعد از رفع چه کنم؟

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

دو نتیجه‌ی عملی: اول، اگر قرار است سایت را عمداً برای تعمیر پایین بیاورید، ۵۰۰ کد اشتباهی است — 503 با هدر Retry-After به خزنده می‌گوید «بعداً بیا» و این پیام برای ایندکس بی‌خطرتر است. دوم، بعد از رفع خطا در Search Console گزارش «Pages» را برای ردیف «Server error (5xx)» و گزارش «Crawl stats» را برای بازگشت نرخ خزش چک کنید؛ چند روز طول می‌کشد تا نمودار به حالت قبل برگردد و این تأخیر طبیعی است. و اگر جلوی سایت CDN یا افزونه‌ی کش دارید، بعد از رفع خطا یک بار کش را خالی کنید و با همان دستور curl از چند صفحه‌ی مختلف — نه فقط صفحه‌ی اصلی — کد 200 بگیرید.

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

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

  1. کد واقعی را بگیرید: curl -s -o /dev/null -w "%{http_code}\n" https://example.com/ — شاید اصلاً ۵۰۲ یا ۵۰۳ باشد و مسیرتان عوض شود.
  2. لاگ را زنده کنید: tail -f روی لاگ خطای وب‌سرور، و همزمان صفحه را یک بار باز کنید.
  3. خط خطا را بخوانید: عبارت Fatal error و مسیر فایل، معمولاً پرونده را همین‌جا می‌بندد؛ AH00124 یا redirection cycle یعنی مقصر قانون بازنویسی است، نه PHP.
  4. بپرسید «چه چیزی عوض شده؟»: آپدیت افزونه، دیپلوی، ارتقای PHP — آخرین تغییر، مظنون اول است.
  5. افزونه‌ی مشکوک را از SSH خاموش کنید: تغییر نام پوشه‌اش در wp-content/plugins کافی است.
  6. روی آپاچی/لایت‌اسپید .htaccess را موقتاً کنار بگذارید — و اگر فقط پیشخوان خراب است، wp-admin/.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 تست بگیرید. اگر سه چیز را با هم عوض کنید و سایت برگردد، نمی‌فهمید کدام درمان بود — و دفعه‌ی بعد باید از صفر شروع کنید.

قدم بعدی

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

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

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

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

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

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

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

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

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

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

آیا خطای 500 روی سئو و رتبه‌ی سایت تأثیر دارد؟

بله، اما تأثیرش به مدت خطا بستگی دارد: طبق مستندات گوگل، خطاهای 5xx باعث می‌شوند خزنده موقتاً سرعت خزش سایت را کم کند و محتوای صفحه‌های خطادار را نادیده بگیرد؛ صفحه‌های ایندکس‌شده اول در ایندکس می‌مانند ولی اگر خطا ادامه پیدا کند در نهایت حذف می‌شوند. یک قطعی چند دقیقه‌ای عملاً بی‌اثر است، قطعی چندروزه نه. بعد از رفع خطا نرخ خزش تدریجی برمی‌گردد و ردیف «Server error (5xx)» در Search Console باید طی چند روز خالی شود.

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

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

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