MAROON ACADEMY

رفع خطای 502 Bad Gateway در Nginx و PHP-FPM

راهنمای عملی تشخیص و رفع خطای 502 در Nginx؛ از بررسی لاگ و وضعیت PHP-FPM تا اصلاح socket، permission، timeout و ظرفیت workerها.

امنیت سرور

خطای 502 Bad Gateway در Nginx یعنی Nginx درخواست را دریافت کرده، اما از upstream مانند PHP-FPM پاسخ معتبر نگرفته است. راه‌حل معمول، زیادکردن timeout یا restart کورکورانه نیست؛ ابتدا همان درخواست را بازتولید کنید، لاگ Nginx و PHP-FPM را بخوانید و سپس سرویس، مسیر socket، مجوز، timeout یا ظرفیت workerها را بر اساس پیام واقعی اصلاح کنید.

502 با خطای 500 چه تفاوتی دارد؟

خطای 500 معمولاً داخل برنامه یا PHP ایجاد می‌شود و upstream توانسته پاسخی به وب‌سرور بدهد. در 502، ارتباط Nginx با upstream قطع، رد یا نامعتبر شده است. اگر Nginx به PHP-FPM متصل نشود، socket وجود نداشته باشد، worker هنگام پاسخ crash کند یا header ناقص بفرستد، کاربر 502 می‌بیند. خطای 504 نیز معمولاً به پایان زمان انتظار gateway اشاره دارد و مسیر تشخیص نزدیک اما یکسان نیست.

مرحله اول: خطا را دقیق و قابل تکرار کنید

زمان، URL، روش درخواست و اینکه خطا دائمی یا فقط زیر بار رخ می‌دهد را ثبت کنید:

curl -I https://example.com
curl -sS -o /dev/null -w 'status=%{http_code} time=%{time_total}\n' \
  https://example.com/problem-page

دامنه و مسیر واقعی را جایگزین کنید. اگر فقط یک عملیات سنگین مانند گزارش‌گیری، import یا checkout خراب است، احتمال timeout، مصرف حافظه یا crash بیشتر می‌شود. اگر همه صفحات PHP خراب‌اند اما فایل استاتیک باز می‌شود، ابتدا PHP-FPM و socket را بررسی کنید.

مرحله دوم: لاگ را منبع حقیقت قرار دهید

sudo tail -n 100 /var/log/nginx/error.log
systemctl list-units --type=service --all | grep -E 'php.*fpm'
sudo journalctl -u php8.3-fpm --since '15 minutes ago' --no-pager

عدد نسخه را با سرویس خود هماهنگ کنید. پیام connect() failed (2: No such file or directory) به مسیر اشتباه یا سرویس خاموش اشاره می‌کند؛ Permission denied مشکل مالکیت socket است؛ Connection refused یعنی چیزی روی مقصد گوش نمی‌دهد؛ و upstream timed out نشان می‌دهد در فاصله تعیین‌شده پاسخی نرسیده است. لاگ ممکن است مسیر، IP یا داده حساس داشته باشد و نباید عمومی منتشر شود.

مسیر عیب‌یابی خطای 502 از Nginx و socket تا PHP-FPM و لاگ برنامههر لایه را به‌ترتیب بررسی کنید؛ تغییر هم‌زمان چند تنظیم علت واقعی را پنهان می‌کند.

مرحله سوم: سلامت PHP-FPM را بررسی کنید

sudo systemctl status php8.3-fpm --no-pager
sudo systemctl is-active php8.3-fpm
sudo php-fpm8.3 -t
sudo systemctl restart php8.3-fpm

فقط وقتی تست پیکربندی موفق است restart کنید. اگر سرویس دوباره متوقف شد، journal را بخوانید و علت‌هایی مثل syntax اشتباه، کمبود حافظه، extension معیوب یا crash worker را رفع کنید. restart می‌تواند موقتاً نشانه را پاک کند، اما جای تحلیل علت نیست.

مرحله چهارم: مسیر socket را بین Nginx و FPM تطبیق دهید

مقصد واقعی Nginx و مقدار listen در pool را مقایسه کنید:

sudo grep -RIn 'fastcgi_pass' /etc/nginx/sites-enabled /etc/nginx/conf.d
sudo grep -RIn '^listen\s*=' /etc/php/8.3/fpm/pool.d
sudo ss -xl | grep php
sudo ls -l /run/php/

اگر Nginx به /run/php/php8.2-fpm.sock اشاره می‌کند ولی سرویس فعال socket نسخه 8.3 را ساخته، fastcgi_pass را به مسیر درست تغییر دهید. در حالت TCP، هر دو سمت باید روی همان IP و پورت باشند. مستندات رسمی PHP هشدار می‌دهد FastCGI را روی اینترنت عمومی باز نکنید؛ روی یک سرور، Unix socket انتخاب امن‌تری است.

مجوز socket را امن اصلاح کنید

کاربر workerهای Nginx را با ps ببینید و مقادیر pool را بررسی کنید:

ps -eo user,group,comm | grep nginx
sudo grep -E '^(listen.owner|listen.group|listen.mode)' \
  /etc/php/8.3/fpm/pool.d/www.conf

در Ubuntu معمولاً Nginx و FPM با گروه www-data هماهنگ می‌شوند و mode socket اغلب 0660 است. مقدار درست به معماری شما بستگی دارد. استفاده از chmod 777 یا بازکردن FPM روی 0.0.0.0:9000 راه‌حل امنی نیست.

مرحله پنجم: تنظیم Nginx را پیش از reload آزمایش کنید

sudo nginx -t
sudo systemctl reload nginx
sudo systemctl is-active nginx php8.3-fpm

فرمان nginx -t باید syntax و امکان بازکردن فایل‌های referenced را موفق اعلام کند. reload پس از تست، workerهای جدید را بالا می‌آورد و workerهای قدیمی اتصال‌های فعال را کامل می‌کنند. اگر تست شکست خورد، reload نکنید و همان فایل و خط گزارش‌شده را اصلاح کنید.

مرحله ششم: timeout را فقط برای درخواست واقعاً طولانی تغییر دهید

مقدار پیش‌فرض fastcgi_read_timeout در Nginx برابر ۶۰ ثانیه است و فاصله بین دو عملیات خواندن از upstream را می‌سنجد، نه کل زمان دانلود پاسخ. اگر لاگ دقیقاً upstream timed out دارد و عملیات باید طولانی باشد، می‌توانید در همان location مقدار محدود و مستند تعیین کنید:

location ~ ^/index\.php(/|$) {
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
    fastcgi_read_timeout 120s;
}

سپس sudo nginx -t و reload را اجرا کنید. افزایش timeout برای query کند، loop، API معطل یا کمبود worker درمان نیست؛ ابتدا زمان اجرای برنامه، دیتابیس و سرویس خارجی را اندازه بگیرید.

مرحله هفتم: اشباع workerهای PHP-FPM را تشخیص دهید

اگر 502 فقط در ترافیک بالا دیده می‌شود، پیام رسیدن به pm.max_children را در journal یا لاگ FPM جست‌وجو کنید:

sudo journalctl -u php8.3-fpm --since '1 hour ago' --no-pager \
  | grep -Ei 'max_children|busy|warning|error'
free -h
ps --no-headers -o rss -C php-fpm8.3 | awk '{sum+=$1; n++} END {if(n) print sum/n " KB average"}'

pm.max_children سقف درخواست‌های هم‌زمان هر pool است. آن را بر اساس RAM قابل‌استفاده و مصرف واقعی هر worker تنظیم کنید، نه یک عدد کپی‌شده از اینترنت. افزایش زیاد می‌تواند سرور را وارد swap یا OOM کند و 502 بیشتری بسازد. قبل از تغییر، از فایل pool بکاپ بگیرید و پس از php-fpm8.3 -t سرویس را restart کنید.

بررسی نهایی پس از رفع خطا

curl -fsS -o /dev/null -w '%{http_code}\n' https://example.com
sudo journalctl -u php8.3-fpm --since '5 minutes ago' --no-pager
sudo tail -n 50 /var/log/nginx/error.log

فقط صفحه اصلی را تست نکنید؛ همان URL و عملیاتی که 502 می‌داد، ورود، فرم‌ها و endpoint سلامت برنامه را نیز بررسی کنید. نرخ 5xx، مصرف حافظه و زمان پاسخ را چند دقیقه زیر نظر بگیرید. اگر مشکل پس از deploy شروع شده، تغییرات نسخه PHP، extension، env و فرایندهای طولانی‌مدت را با release قبلی مقایسه کنید.

جمع‌بندی

برای رفع 502 در Nginx، ابتدا پیام error log را به یکی از شاخه‌های سرویس خاموش، socket اشتباه، permission، timeout یا ظرفیت ناکافی نگاشت کنید. پیکربندی PHP-FPM و Nginx را پیش از restart و reload آزمایش کنید و timeout یا worker را تنها با شواهد و اندازه‌گیری تغییر دهید. این روش هم قطعی را کوتاه می‌کند و هم از بازگشت دوباره خطا جلوگیری می‌کند.

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

منابع رسمی: ماژول FastCGI در Nginx، کنترل و reload در Nginx، تنظیمات رسمی PHP-FPM، معرفی PHP-FPM و راهنمای journalctl.