ESC را فشار دهید تا بسته شود

رفع خطای 504 Gateway Timeout در NGINX؛ عیب‌یابی Upstream، proxy_read_timeout و PHP-FPM

فهرست

اگر NGINX پاسخ 504 Gateway Timeout می‌دهد، نخست لاگ خطا را بررسی کنید و مشخص کنید کدام Upstream در زمان مقرر پاسخ نداده است. مسیر تشخیص عملی این است: زمان رخداد و URL → error.log → تست مستقیم Backend → تشخیص proxy یا FastCGI → بررسی منابع و لاگ برنامه → اصلاح علت → nginx -t و Reload → Verify. زیادکردن کورکورانه Timeout ممکن است فقط خرابی را دیرتر به کاربر نشان دهد.

سناریوی واقعی و تفاوت 504 با 502

فرض کنید NGINX روی Ubuntu درخواست‌های HTTPS را به برنامه‌ای در 127.0.0.1:8000 یا PHP-FPM می‌فرستد. صفحه‌های ساده سریع باز می‌شوند اما گزارش سنگین یا درخواست پایگاه‌داده پس از مدتی با 504 متوقف می‌شود. در 504، در یکی از مراحل ارتباط با Upstream مهلت انتظار تمام شده است؛ در 502 معمولاً اتصال یا پاسخ Upstream نامعتبر، قطع‌شده یا ناموفق است. برای حالت دوم، راهنمای مستقل عیب‌یابی خطای 502 در NGINX و بررسی Socket و Upstream را بخوانید.

پیش‌نیازها و اقدامات ایمن قبل از تغییر

  • دسترسی SSH و مجوز sudo برای دیدن کانفیگ و لاگ NGINX.
  • آگاهی از معماری مسیر درخواست: NGINX → اپلیکیشن HTTP، یا NGINX → PHP-FPM/FastCGI، یا چند لایه Reverse Proxy/CDN.
  • ثبت URL و زمان خطا، نام سرویس Backend و بازه‌ای برای تغییر کنترل‌شده.
  • بکاپ کانفیگ و امکان بازگشت؛ در محیط Production ابتدا روی Staging یا یک مسیر کم‌خطر تست کنید.
sudo nginx -t
sudo sh -c 'nginx -T > /root/nginx-config-before-504.txt'
sudo systemctl status nginx --no-pager
date -Is

فایل خروجی nginx -T ممکن است مسیرهای داخلی، Headerها یا داده‌های حساس کانفیگ را نشان دهد؛ آن را عمومی منتشر نکنید. اگر با ساختار فایل‌های NGINX آشنا نیستید، آموزش نصب و کانفیگ NGINX روی Ubuntu را پیش از ویرایش مرور کنید.

مرحله ۱: علت واقعی 504 را در error.log پیدا کنید

sudo tail -n 120 /var/log/nginx/error.log
sudo journalctl -u nginx -n 100 --no-pager
sudo nginx -T | grep -nE 'proxy_pass|fastcgi_pass|proxy_read_timeout|proxy_connect_timeout|proxy_send_timeout|fastcgi_read_timeout'

در نصب‌های سفارشی، مسیر لاگ ممکن است در error_log کانفیگ تغییر کرده باشد. هم‌زمان با تکرار درخواست، پیام‌هایی مانند upstream timed out (110: Connection timed out) while reading response header from upstream را جستجو کنید. عبارت while reading response header یعنی NGINX پس از اتصال، در انتظار Header پاسخ مانده است؛ عبارت while connecting to upstream به مرحله اتصال اشاره می‌کند. این دو مسیر عیب‌یابی یکسان نیستند.

مرحله ۲: Backend را بدون NGINX آزمایش کنید

اگر Upstream یک سرویس HTTP محلی است، پورت Listen و پاسخ مستقیم آن را بررسی کنید. آدرس و مسیر /health در مثال باید با برنامه واقعی جایگزین شوند؛ HTTP 404 از مسیر نمونه لزوماً به معنای خرابی ارتباط نیست.

sudo ss -lntp
curl -v --connect-timeout 5 --max-time 20 http://127.0.0.1:8000/health
time curl -sS -o /dev/null -w 'status=%{http_code} total=%{time_total}s' http://127.0.0.1:8000/health
sudo systemctl status myapp --no-pager
sudo journalctl -u myapp -n 120 --no-pager

اگر درخواست مستقیم هم کند یا Timeout می‌شود، قبل از دست‌کاری NGINX، Queryهای پایگاه‌داده، Queue، Thread/Workerها، قفل‌ها، Dependencyهای بیرونی و خطاهای برنامه را بررسی کنید. اگر مستقیم سریع است اما از مسیر NGINX کند می‌شود، آدرس Upstream، DNS، لایه‌های واسط، تنظیمات Proxy و تفاوت Headerها را بررسی کنید.

مرحله ۳: تفاوت proxy_read_timeout و proxy_connect_timeout را بفهمید

طبق مستندات رسمی NGINX برای proxy_read_timeout، این مقدار زمان انتظار بین دو عملیات خواندن متوالی از سرور پروکسی‌شده است، نه الزاماً سقف کل مدت پاسخ. مقدار پیش‌فرض معمول ۶۰ ثانیه است. proxy_connect_timeout مهلت برقراری اتصال و proxy_send_timeout مهلت بین عملیات ارسال درخواست به Upstream را کنترل می‌کنند. بالا بردن همه این اعداد بدون دانستن مرحله Timeout راه‌حل اصولی نیست.

پس از رفع کندی Backend، اگر درخواست‌های معتبر واقعاً بیش از مقدار پیش‌فرض به زمان نیاز دارند، می‌توانید فقط در Location مربوط و با سقف متناسب تنظیم کنید. نمونه زیر برای HTTP Reverse Proxy است، نه PHP-FPM:

location / {
    proxy_pass http://127.0.0.1:8000;
    proxy_connect_timeout 5s;
    proxy_send_timeout 30s;
    proxy_read_timeout 75s;
}

این اعداد صرفاً نمونه‌اند و باید با SLO برنامه، محدودیت Load Balancer/CDN و ظرفیت Workerها هماهنگ شوند. اگر درخواست چند دقیقه طول می‌کشد، اجرای Job در پس‌زمینه و Polling نتیجه اغلب از افزایش Timeout بهتر است.

مرحله ۴: اگر PHP-FPM دارید، تنظیم درست FastCGI را بررسی کنید

در کانفیگ‌های fastcgi_pass، دستور مرتبط با خواندن پاسخ fastcgi_read_timeout است، نه proxy_read_timeout. برای تشخیص، هم کانفیگ NGINX و هم وضعیت سرویس PHP-FPM و لاگ Pool را بررسی کنید. نام Unit بسته به نسخه PHP و توزیع متفاوت است.

sudo nginx -T | grep -nE 'fastcgi_pass|fastcgi_read_timeout'
systemctl list-units 'php*-fpm.service' --all
sudo journalctl -u php8.3-fpm -n 100 --no-pager
free -h
df -h

اگر نسخه شما PHP 8.3 نیست، نام Unit را جایگزین کنید. کندی Workerها، صف درخواست، محدودیت pm.max_children و اجرای طولانی PHP را با راهنمای عیب‌یابی PHP-FPM با Slow Log بررسی کنید. نمونه تنظیم برای Location واقعی PHP، پس از تأیید علت و سازگاری با تنظیمات Pool:

location ~ [.]php$ {
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_read_timeout 75s;
}

هشدار: بلوک بالا نمونه آموزشی است؛ آن را بدون تطبیق با فایل‌های Include، نسخه PHP، مسیر Socket، قواعد امنیتی و Locationهای فعلی در Production جایگزین نکنید. ممکن است کانفیگ موجود از snippets/fastcgi-php.conf استفاده کند. fastcgi_read_timeout نیز سقف کل اجرای PHP نیست و محدودیت‌های max_execution_time یا PHP-FPM مستقل‌اند.

مرحله ۵: CPU، RAM، دیسک و محدودیت اتصال را بررسی کنید

uptime
free -h
df -h
df -i
sudo ss -s
sudo journalctl -k -n 80 --no-pager

اگر Load بالا، کمبود حافظه، پرشدن دیسک یا Inode و پیام OOM دیده می‌شود، رفع مشکل منابع را بر افزایش Timeout مقدم بدانید. در معماری چندسروری، لاگ‌های Backend و شبکه بین NGINX و Upstream را در همان بازه زمانی مقایسه کنید.

اگر NGINX روی VPS خودمدیریت اجرا می‌شود

وقتی Reverse Proxy و Backend روی VPS خودمدیریت هستند، امکان دسترسی به لاگ‌ها، تنظیم Firewall، مشاهده مصرف CPU/RAM و دسترسی کنسول هنگام قطعی برای انتخاب زیرساخت مهم است. پس از شناسایی ریشه مشکل، می‌توانید پلن‌های سرور مجازی وان‌سرور را از نظر منابع و سطح مدیریت موردنیاز بررسی کنید؛ اما خرید VPS جدید به‌تنهایی Query کند، بن‌بست Worker یا تنظیم اشتباه Timeout را برطرف نمی‌کند. برای هاست اشتراکی که دسترسی NGINX ندارید، بررسی با پشتیبانی میزبان ضروری است.

مرحله ۶: تغییر را اعتبارسنجی و بدون قطع سرویس Reload کنید

پس از اصلاح محدود و مستند، اول Syntax را تست کنید؛ فقط در صورت موفقیت Reload انجام دهید. سپس همان URL مشکل‌دار را چند بار با زمان‌سنجی تست و لاگ جدید را با قبل مقایسه کنید.

sudo nginx -t
sudo systemctl reload nginx
curl -I --connect-timeout 5 --max-time 30 https://example.com/
sudo tail -n 80 /var/log/nginx/error.log

تست curl -I درخواست HEAD است و ممکن است مسیرهای POST یا API سنگین را پوشش ندهد. برای Verify واقعی، همان Method و ورودی کنترل‌شده‌ای را که قبلاً 504 ایجاد می‌کرد دوباره آزمایش کنید. نتیجه مطلوب: پاسخ معتبر، کاهش زمان پاسخ، نبود خطای جدید Upstream و پایداربودن مصرف منابع.

Rollback امن در صورت بدترشدن وضعیت

اگر تغییر فقط در یک فایل مشخص انجام شده، نسخه پشتیبان همان فایل را برگردانید؛ فایل خروجی nginx -T برای مستندسازی است و نباید به‌جای کانفیگ اجرایی در nginx.conf کپی شود. نمونه زیر را با مسیر واقعی فایل و بکاپ خود جایگزین کنید:

sudo cp /etc/nginx/sites-available/example.com.bak /etc/nginx/sites-available/example.com
sudo nginx -t
sudo systemctl reload nginx

اگر nginx -t شکست خورد، Reload نکنید. اگر علت اصلی در Backend، پایگاه‌داده یا منابع بوده، بازگرداندن Timeout به‌تنهایی مشکل را رفع نمی‌کند؛ تغییرات آن بخش‌ها باید با برنامه Rollback مستقل برگردند.

اشتباه‌های رایج در رفع 504

  • افزایش هم‌زمان همه Timeoutها بدون بررسی error.log و کندی Backend.
  • تنظیم proxy_read_timeout برای سایتی که در واقع fastcgi_pass دارد.
  • فرض اینکه 504 حتماً از NGINX مبدا آمده؛ CDN یا Load Balancer جلویی هم ممکن است 504 برگرداند.
  • اجرای دستور Restart به‌جای تحلیل Queue، OOM یا Query کند.
  • Reload بدون nginx -t و بدون امکان بازگشت.
  • انتظار اینکه یک مقدار Timeout کل زمان درخواست را محدود کند؛ رفتار هر Directive و هر لایه متفاوت است.

مبنای فنی این راهنما مستندات رسمی NGINX درباره HTTP Proxy Module و FastCGI Module است. پیش از تغییر در سرور حساس، دستورها و مسیرها را با نسخه و معماری واقعی تطبیق دهید.

Rate this post
اشتراک گذاری نوشته در:

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *