عیب‌یابی

رفع خطای 502 Bad Gateway — عیب‌یابی قدم‌به‌قدم از لاگ Nginx تا کلادفلر

رفع خطای 502 Bad Gateway: نمایی از زنجیره‌ی مرورگر تا وب‌سرور Nginx و بک‌اند PHP-FPM با نقطه‌ی قطع‌شده، در کنار لاگ خطای Nginx، وضعیت سرویس بک‌اند و نشانه‌ی کمبود رم روی سرور لینوکس

صفحه‌ی سفید با عبارت «502 Bad Gateway» یعنی وب‌سرور سرِ پا است، اما از سرویس پشت خودش جواب درستی نگرفته — و همین سرنخ، نصف مسیر عیب‌یابی است. در این راهنما اول به زبان ساده می‌گوییم این خطا (و برادرهایش ۵۰۳ و ۵۰۴) دقیقاً یعنی چه، بعد یک قیف تشخیص عملی را قدم‌به‌قدم طی می‌کنیم: از ری‌استارت PHP-FPM و خواندن لاگ Nginx تا شکار OOM Killer و تشخیص اینکه مشکل از کلادفلر است یا سرور اصلی.

خطای 502 یعنی چه؟ (به زبان ساده)

سایت‌های مدرن معمولاً دولایه‌اند: جلوی صحنه یک وب‌سرور یا پراکسی (اغلب Nginx، گاهی کلادفلر یا HAProxy) نشسته و پشت صحنه یک «بک‌اند» کار اصلی را می‌کند — PHP-FPM برای وردپرس، یک اپ Node.js، یا Gunicorn برای جنگو. وقتی پراکسی درخواست شما را به بک‌اند می‌فرستد و در جواب یا هیچ نمی‌گیرد، یا اتصال رد می‌شود، یا پاسخِ خراب و ناقص برمی‌گردد، دست خالی به مرورگر می‌گوید: 502 Bad Gateway — «من دروازه‌ام، ولی آن‌طرفِ دروازه جواب درستی نداد».

پس نکته‌ی کلیدی: در خطای ۵۰۲ تقریباً همیشه وب‌سرور سالم است و بک‌اند مشکل دارد — مرده، در حال ری‌استارت است، رمش تمام شده یا Nginx اصلاً آدرسش را اشتباه دارد. سه خطای هم‌خانواده را هم از هم جدا کنیم:

کدمعنیمقصر معمول
502 Bad Gatewayپراکسی به بک‌اند وصل شد ولی جواب سالمی نگرفت (یا اصلاً اتصال رد شد)بک‌اند کرش‌کرده، سوکت اشتباه، کمبود رم
503 Service Unavailableسرویس عمداً یا از فشار زیاد «فعلاً در دسترس نیست»حالت تعمیر، اشباع منابع، محدودیت هاست اشتراکی
504 Gateway Timeoutبک‌اند زنده است ولی جوابش آن‌قدر طول کشید که پراکسی خسته شدکوئری سنگین، اسکریپت کند، تایم‌اوت کوتاه

این تفکیک مهم است چون درمان‌ها فرق دارند: ۵۰۲ یعنی «بک‌اند را زنده کن»، ۵۰۴ یعنی «بک‌اند را سریع‌تر کن یا مهلت بده»، و ۵۰۳ اغلب یعنی «ظرفیت کم آورده‌ای». اگر ۵۰۳ گرفتن‌های مکرر روی هاست اشتراکی آزارتان می‌دهد، آن داستان جدایی است که در مقاله‌ی هاست اشتراکی یا سرور مجازی؟ کامل باز کرده‌ایم.

اگر فقط بازدیدکننده‌اید، این چهار کار را بکنید

خطای ۵۰۲ در بیش از ۹۰ درصد موارد مشکل خودِ سایت است، نه اینترنت شما. اما قبل از قضاوت:

  1. یک بار رفرش کنید — اگر بک‌اند در حال ری‌استارت بوده (مثلاً وسط دیپلوی)، چند ثانیه بعد سایت برمی‌گردد. رفرش سخت با Ctrl+F5 کش مرورگر را هم دور می‌زند.
  2. از شبکه‌ی دیگری تست کنید — با اینترنت موبایل باز کنید. اگر آنجا باز شد، مشکل از DNS یا مسیر شبکه‌ی شماست، نه سایت.
  3. کش DNS را خالی کنید — در ویندوز: ipconfig /flushdns در PowerShell. شاید آی‌پی قدیمیِ یک سرورِ ازکارافتاده در کش شما مانده باشد.
  4. چند دقیقه صبر کنید — ۵۰۲ معمولاً موقتی است؛ مدیر سایت احتمالاً همین حالا دارد سرورش را ری‌استارت می‌کند.

اگر با همه‌ی این‌ها خطا پابرجاست، سایت واقعاً پایین است — و اگر آن سایت مال شماست، ادامه‌ی این مقاله دقیقاً برای شما نوشته شده.

اگر مدیر سایتید: قیف تشخیص از بالا به پایین

عیب‌یابی ۵۰۲ حدس‌زدن نیست؛ یک مسیر ثابت است که هر بار همان را می‌روید — از سریع‌ترین چک به عمیق‌ترین:

💡 چک‌لیست ۶۰ ثانیه‌ای: ۱) systemctl status php8.3-fpm (یا سرویس بک‌اندتان) — زنده است؟ ۲) tail -n 30 /var/log/nginx/error.log — پیام دقیق چیست؟ ۳) free -h — رم تمام نشده؟ ۴) dmesg | grep -i kill — چیزی OOM-kill نشده؟ ۵) تازگی دیپلوی یا آپدیت کرده‌اید؟ همان را مظنون اول بگیرید. در ادامه هر مورد را باز می‌کنیم.

و اگر SSH هم بالا نمی‌آید (سرور کاملاً قفل شده)، کنسول تحت وب پنل مهران هاست راه نجات است: از مرورگر مستقیم به سرور می‌رسید و ری‌استارت می‌کنید — بدون وابستگی به SSH.

قدم اول: لاگ خطای Nginx حقیقت را می‌گوید

هرگز کورکورانه ری‌استارت نکنید؛ اول ببینید Nginx خودش چه می‌گوید:

sudo tail -n 30 /var/log/nginx/error.log

سه پیام، پرونده‌ی اکثر ۵۰۲‌ها را می‌بندند:

connect() failed (111: Connection refused) while connecting to upstream

یعنی Nginx در زد ولی کسی خانه نبود: بک‌اند (PHP-FPM یا اپ Node) مرده یا روی آن پورت/سوکت گوش نمی‌دهد. مستقیم بروید سراغ بخش بعدی.

connect() to unix:/run/php/php8.2-fpm.sock failed (2: No such file or directory)

یعنی Nginx به سوکتی اشاره می‌کند که وجود ندارد — کلاسیک‌ترین حالتش بعد از ارتقای PHP است: کانفیگ هنوز php8.2 را صدا می‌زند در حالی که روی سرور فقط php8.3-fpm نصب است.

upstream sent too big header while reading response header from upstream

یعنی بک‌اند زنده است ولی هدرِ پاسخش (اغلب کوکی‌ها و ری‌دایرکت‌های سنگین وردپرس یا افزونه‌های امنیتی) از بافر Nginx بزرگ‌تر شده. درمانش در بلاک location مربوط به PHP:

fastcgi_buffer_size 32k;
fastcgi_buffers 16 32k;
# و برای reverse proxy معمولی:
proxy_buffer_size 32k;
proxy_buffers 16 32k;

بعد از هر تغییر کانفیگ: sudo nginx -t && sudo systemctl reload nginx.

شایع‌ترین مقصر: PHP-FPM خوابیده است

در سرورهای وردپرسی و اغلب سایت‌های PHP، خطای ۵۰۲ تقریباً مترادف است با «PHP-FPM بالا نیست». وضعیتش را ببینید:

systemctl status php8.3-fpm
# اگر خاموش است:
sudo systemctl restart php8.3-fpm
# و مطمئن شوید بعد از ریبوت هم خودش بالا بیاید:
sudo systemctl enable php8.3-fpm

اگر سرویس بالا نمی‌آید، دلیلش را از ژورنال بخوانید — معمولاً یک خطای سینتکس در کانفیگ pool یا یک اکستنشن خراب است:

journalctl -u php8.3-fpm -n 30 --no-pager

ناهماهنگی مسیر سوکت — قاتل خاموش بعد از ارتقا

Nginx و PHP-FPM از طریق یک سوکت یونیکس حرف می‌زنند و اسم این سوکت باید در هر دو طرف یکی باشد. دو طرف را مقایسه کنید:

# Nginx به کجا می‌فرستد؟
grep -r fastcgi_pass /etc/nginx/sites-enabled/
# PHP-FPM واقعاً کجا گوش می‌دهد؟
grep "^listen" /etc/php/8.3/fpm/pool.d/www.conf
ls /run/php/

در اوبونتو مسیر درست unix:/run/php/php8.3-fpm.sock است؛ در آلمالینوکس ۹ داستان فرق دارد: سرویس فقط php-fpm نام دارد و سوکت پیش‌فرضش /run/php-fpm/www.sock است. اگر کانفیگ از توزیع دیگری کپی شده باشد، همین ناهماهنگی کوچک یعنی ۵۰۲ دائمی.

💡 نکته: بعد از هر ارتقای نسخه‌ی PHP، یک grep -r "php8" /etc/nginx/ بزنید و تمام ارجاع‌ها را به نسخه‌ی جدید برسانید. نیمی از ۵۰۲‌های «بعد از آپدیت دیشب» همین‌جا حل می‌شوند. راه‌اندازی درست و تمیز این زوج را در راهنمای Nginx روی اوبونتو قدم‌به‌قدم آورده‌ایم.

بک‌اند Node یا Gunicorn پشت reverse proxy

اگر Nginx با proxy_pass http://127.0.0.1:3000; به یک اپ Node، Gunicorn یا کانتینر داکری وصل است، اول چک کنید اصلاً کسی روی آن پورت گوش می‌دهد یا نه:

ss -tlnp | grep 3000

خروجی خالی یعنی اپ کرش کرده. لاگش را ببینید (journalctl -u myapp -n 50 برای سرویس systemd، pm2 logs برای PM2، یا docker logs اگر کانتینری است — راهنمای داکر روی سرور ابری را ببینید). سه علت پرتکرار کرشِ اپ بعد از دیپلوی: متغیر محیطی جاافتاده، پورت اشغال‌شده (EADDRINUSE در لاگ Node) و نسخه‌ی ناسازگار وابستگی‌ها. یک نکته‌ی ظریف: اگر اپ فقط روی localhost داخل کانتینر گوش بدهد ولی Nginx بیرون کانتینر باشد، اتصال رد می‌شود — اپ باید روی 0.0.0.0 گوش بدهد یا پورتش درست map شده باشد.

رم تمام شده؟ ردپای OOM Killer را بگیرید

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

dmesg | grep -i "killed process"
# یا:
journalctl -k | grep -i oom

اگر خطی شبیه این دیدید، پرونده بسته است:

Out of memory: Killed process 1471 (mysqld) total-vm:1834244kB

وضعیت فعلی رم را هم ببینید (تفسیر کامل خروجی free و بقیه‌ی ابزارهای پایش را در راهنمای مانیتورینگ سرور لینوکس آورده‌ایم):

free -h

درمان کوتاه‌مدت: ری‌استارت سرویسِ کشته‌شده و ساخت یک سواپ ۲ گیگابایتی تا ضربه‌گیر داشته باشید:

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile && sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

درمان واقعی اما یکی از این دو است: مصرف را کم کنید (کش درست، محدودکردن pm.max_children، تیون MySQL) یا رم را زیاد کنید. مزیت سرور ابری این است که ارتقای رم چند کلیک است، نه مهاجرت؛ فقط بدانید ارتقا یک‌طرفه است و کاهش منابع بعداً امکان‌پذیر نیست، پس یک پله بالاتر بروید نه سه پله.

محدودیت‌های PHP-FPM: وقتی صف پر می‌شود

PHP-FPM تعداد محدودی worker دارد (pm.max_children). وقتی همه مشغول‌اند، درخواست‌های جدید در صف می‌مانند و اگر صف هم لبریز شود یا worker ها یکی‌یکی کرش کنند، Nginx خطای ۵۰۲ یا ۵۰۴ تحویل می‌دهد. نشانه‌اش در لاگ خود FPM است:

sudo tail -n 20 /var/log/php8.3-fpm.log
[pool www] server reached pm.max_children setting (5), consider raising it

عدد مناسب را از رم آزاد حساب کنید: مصرف هر worker وردپرسی معمولاً ۶۰ تا ۹۰ مگابایت است؛ پس روی سروری با ~۲ گیگ رم آزاد، pm.max_children = 20 عددی منطقی است. در /etc/php/8.3/fpm/pool.d/www.conf:

pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 8

و بعد sudo systemctl reload php8.3-fpm. بالا بردن بی‌حساب این عدد هم خودش OOM می‌آورد — همان دور باطل بخش قبل. اگر سایت وردپرسی‌تان مدام به این سقف می‌خورد، قبل از خرید رم، کش صفحه و OPcache را فعال کنید؛ کش خوب یعنی اکثر بازدیدها اصلاً به PHP نمی‌رسند.

تایم‌اوت‌ها: وقتی ۵۰۲ در واقع ۵۰۴ است

اگر خطا بعد از یک مکث طولانی (مثلاً دقیقاً ۶۰ ثانیه) ظاهر می‌شود، با تایم‌اوت طرفید نه کرش. Nginx به‌طور پیش‌فرض ۶۰ ثانیه منتظر جواب بک‌اند می‌ماند؛ برای عملیات‌های ذاتاً کند (ایمپورت بزرگ ووکامرس، ساخت بکاپ از داخل وردپرس، گزارش سنگین) این مهلت را هدفمند و فقط در همان بلاک زیاد کنید:

# پشت fastcgi (PHP):
fastcgi_read_timeout 300;
# پشت reverse proxy (Node/Gunicorn):
proxy_read_timeout 300;
proxy_connect_timeout 10;

حواستان باشد PHP هم مهلت خودش را دارد (max_execution_time در php.ini)؛ اگر آن ۳۰ ثانیه بماند، قبل از تایم‌اوت Nginx خود PHP کار را قطع می‌کند. و یک اصل: تایم‌اوت ۳۰۰ ثانیه‌ای راه‌حل نیست، مسکن است — درخواستی که ۵ دقیقه طول می‌کشد یا باید بهینه شود یا به صف پس‌زمینه (cron یا worker) منتقل شود.

۵۰۲ بعد از آپدیت یا دیپلوی: به عقب نگاه کنید

اگر سایت تا دیشب سالم بود و امروز ۵۰۲ می‌دهد، اولین سؤال این نیست «چه چیزی خراب است؟» بلکه این است: «چه چیزی عوض شده؟» مظنون‌های همیشگی:

  • ارتقای PHP — سوکت و نام سرویس عوض شده ولی کانفیگ Nginx قدیمی مانده (بخش سوکت را ببینید)؛ یا یک اکستنشن (مثل php8.3-mysql) بعد از ارتقا نصب نشده.
  • دیپلوی کد جدید — اپ Node/Python هنگام استارت کرش می‌کند؛ لاگ استارتاپ را ببینید، نه فقط لاگ Nginx را.
  • apt upgrade شبانه — سرویسی ری‌استارت شده و بالا نیامده. systemctl --failed فهرست سرویس‌های زمین‌خورده را می‌دهد.
  • تغییر کانفیگ Nginx — همیشه قبل از reload با nginx -t صحت‌سنجی کنید؛ کانفیگ خراب گاهی reload را بی‌اثر می‌کند و شما فکر می‌کنید تغییرتان اعمال شده.
⚠️ قبل از هر آپدیت بزرگ یا دیپلوی، یک راه برگشت داشته باشید: اسنپ‌شات یا بکاپ. با راهنمای بکاپ‌گیری لینوکس، برگرداندن سرور به «حال خوبِ دیشب» چند دقیقه بیشتر طول نمی‌کشد.

۵۰۲ پشت کلادفلر: مشکل از کیست؟

وقتی سایت پشت کلادفلر است، صفحه‌ی خطا خودش می‌گوید مقصر کجاست: اگر پایین صفحه نوشته باشد cloudflare و کادر «Host» با علامت خطا نشان داده شود، یعنی کلادفلر سالم است و سرور اصلی (origin) شما جواب نداده — همه‌ی چک‌های این مقاله را روی سرور خودتان اجرا کنید. برای اطمینان، کلادفلر را دور بزنید و مستقیم از origin تست بگیرید:

curl -I --resolve example.com:443:203.0.113.10 https://example.com/

(به‌جای 203.0.113.10 آی‌پی واقعی سرورتان.) اگر مستقیم هم ۵۰۲ گرفتید، مشکل قطعاً داخل سرور است. اگر مستقیم سالم بود ولی از پشت کلادفلر خطا می‌گیرید، سراغ این دو بروید: فایروال سرور که آی‌پی‌های کلادفلر را بسته، یا ناهماهنگی SSL — حالت رمزنگاری در کلادفلر باید Full (strict) باشد و روی origin گواهی معتبر نصب باشد؛ ناهماهنگی اینجا معمولاً خودش را به‌شکل خطاهای خانواده‌ی 52x (مثل 521 و 526) نشان می‌دهد. اگر تازه DNS را به کلادفلر برده‌اید و رفتار عجیب می‌بینید، مرور راهنمای رکوردهای DNS خالی از لطف نیست.

قدم بعدی

دفعه‌ی بعد که ۵۰۲ دیدید، دیگر سراسیمه ری‌استارت نمی‌کنید: لاگ Nginx را می‌خوانید، وضعیت بک‌اند و رم را چک می‌کنید و در چند دقیقه به ریشه می‌رسید. اگر می‌خواهید این سناریوها را بدون ریسک روی سایت اصلی تمرین کنید — PHP-FPM را عمداً بکُشید، سوکت را خراب کنید و درستش کنید — یک سرور ابری ساعتی بسازید و بعد از تمرین حذفش کنید؛ محاسبه‌ی ساعتی تا لحظه‌ی حذف ادامه دارد و خاموش‌کردن سرور آن را متوقف نمی‌کند، چون منابع همچنان رزرو می‌ماند. برای اینکه اصلاً کمتر به این صفحه برگردید، راه‌اندازی اصولی Nginx نقطه‌ی شروع خوبی است.

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

خطای 502 Bad Gateway را از کجا شروع کنم و چطور رفع کنم؟

برای رفع خطای 502 اول لاگ خطای وب‌سرور را بخوانید و بعد سراغ سرویس بک‌اند بروید، نه برعکس. مسیر ثابت چهار قدم دارد: tail -n 30 /var/log/nginx/error.log برای دیدن پیام دقیق، systemctl status روی PHP-FPM یا اپ Node برای اطمینان از زنده‌بودنش، free -h برای بررسی رم، و در آخر این پرسش که از دیشب تا حالا چه چیزی روی سرور عوض شده است. در بیشتر موارد همان یک خط لاگ مقصر را مستقیم لو می‌دهد و ری‌استارتِ کورکورانه فقط علت را پنهان می‌کند.

چرا سایت وردپرسی من خطای 502 می‌دهد؟

در وردپرس، خطای ۵۰۲ تقریباً همیشه یعنی PHP-FPM بالا نیست یا ظرفیتش تمام شده است. سه سناریوی رایج: سرویس FPM کرش کرده و با systemctl restart برمی‌گردد؛ بعد از ارتقای نسخه‌ی PHP، کانفیگ وب‌سرور هنوز سوکت نسخه‌ی قبلی را صدا می‌زند؛ یا worker ها به سقف pm.max_children خورده‌اند و صف لبریز شده. حالت چهارم و کم‌ترشناخته‌شده، هدر بیش‌ازحد بزرگ افزونه‌های امنیتی است که با افزایش fastcgi_buffer_size حل می‌شود.

تفاوت خطای 502 با 503 و 504 در چیست؟

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

چرا خطای 502 مدام تکرار می‌شود و بعد خودش خوب می‌شود؟

الگوی «چند ساعت سالم، بعد ۵۰۲، بعد دوباره سالم» تقریباً همیشه امضای کمبود رم یا پرشدن worker های PHP است. وقتی رم تمام می‌شود کرنل لینوکس پرمصرف‌ترین پروسه — معمولاً MySQL یا PHP-FPM — را می‌کُشد و systemd کمی بعد همان سرویس را برمی‌گرداند؛ سایت ظاهراً «خودش خوب می‌شود». مدرکش را با dmesg | grep -i "killed process" ببینید. تا وقتی مصرف را کم یا رم را زیاد نکنید، این چرخه فردا هم تکرار می‌شود.

روی هاست اشتراکی که به SSH دسترسی ندارم، برای رفع خطای 502 چه کاری از دستم برمی‌آید؟

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

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

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

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