Apache و Nginx

رفع خطای 504 Gateway Timeout در Nginx

خطای 504 Gateway Timeout یعنی Nginx درخواست را به PHP-FPM یا یک سرویس دیگر فرستاده، ولی پاسخی که منتظرش بود به موقع نرسیده است. مسیر رفع این خطا معمولا اول پیدا کردن اسکریپت یا کوئری کند در لاگ است و بعد، در صورت لزوم، بالا بردن timeout ها در Nginx و PHP.

نوشته تیم فنی پیکوهاستبه روزرسانی: ۱۹ مهر ۱۴۰۵۸ دقیقه مطالعه

خطای 504 Gateway Timeout زمانی می آید که Nginx درخواست را به PHP-FPM یا یک سرویس بالادستی دیگر داده و در مدتی که برای انتظار تعیین شده، جوابی نگرفته است. سریع ترین راه رفع آن این است که خط upstream timed out را در error.log پیدا کنید، ببینید کدام آدرس کند است و بعد بین دو کار یکی را انتخاب کنید: کند بودن اسکریپت یا کوئری را برطرف کنید، یا اگر کار واقعا طولانی است، timeout مربوط را بالا ببرید. این راهنما برای مدیر VPS یا سرور اختصاصی نوشته شده که به فایل های Nginx دسترسی دارد. اگر فقط هاست cPanel دارید، بخش مربوط به هاست اشتراکی را ببینید.

خطای 504 Gateway Timeout چه معنی دارد

Nginx در بسیاری از سایت ها خودش PHP را اجرا نمی کند. درخواست را از طریق FastCGI به PHP-FPM می فرستد یا با proxy_pass به برنامه ای مثل Node.js یا Gunicorn پاس می دهد. کد 504 یعنی این واسطه منتظر پاسخ ماند و مهلتش تمام شد. خود Nginx سالم است و خطا از سمت پشت آن می آید. اگر به جای آن خطای 502 می بینید، مشکل از جنس دیگری است و راهنمای خطای 502 Bad Gateway در Nginx را بخوانید.

سه حالت بیشتر از بقیه رخ می دهند: صفحه ای که کوئری دیتابیس سنگین دارد، کاری مثل import یا ساخت گزارش که طبیعتا طولانی است، و پر شدن همه workerهای PHP-FPM به طوری که درخواست جدید در صف می ماند.

پیدا کردن علت در لاگ

قبل از دست بردن در تنظیمات، لاگ را بخوانید. مسیر پیش فرض در Ubuntu و Debian فایل /var/log/nginx/error.log است:

sudo tail -n 100 /var/log/nginx/error.log | grep -i upstream

خطی که دنبالش هستید چیزی شبیه این است:

upstream timed out (110: Connection timed out) while reading response header from upstream, client: 203.0.113.5, server: example.com, request: "GET /wp-admin/edit.php HTTP/1.1", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock:", host: "example.com"

سه بخش این خط مهم است. عبارت while reading response header یعنی Nginx وصل شده ولی هدر پاسخ نیامده است. فیلد request همان آدرس کند را نشان می دهد. فیلد upstream می گوید پشت این درخواست PHP-FPM بوده یا یک پروکسی. اگر به جای خواندن هدر عبارت while connecting to upstream را دیدید، اتصال برقرار نشده و مشکل بیشتر شبیه 502 است.

علت اول: اسکریپت یا کوئری کند

بیشتر اوقات timeout بالا بردن راه حل اصلی نیست، چون پشت 504 یک کار کند وجود دارد. دو بررسی کافی است.

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

time curl -s -o /dev/null -H "Host: example.com" https://127.0.0.1/wp-admin/edit.php -k

دوم، ببینید دیتابیس همان لحظه چه می کند. در MySQL یا MariaDB دستور SHOW FULL PROCESSLIST; را اجرا کنید. کوئری ای که ده ها ثانیه در وضعیت Sending data یا Copying to tmp table مانده، مقصر است. اگر slow query log فعال باشد، همان جا هم ثبت شده است. در وردپرس معمولا یک افزونه جستجو، آمار یا بکاپ پشت ماجراست و راه حل آن غیرفعال کردن افزونه یا اضافه کردن ایندکس است. برای سایت فروشگاهی راهنمای افزایش سرعت ووکامرس همین موضوع را مفصل تر می گوید.

PHP-FPM هم slowlog دارد که نشان می دهد اسکریپت در لحظه کند بودن در کدام خط گیر کرده است. در فایل pool (در Ubuntu معمولا /etc/php/8.2/fpm/pool.d/www.conf) این دو خط را اضافه کنید:

request_slowlog_timeout = 10s
slowlog = /var/log/php8.2-fpm.slow.log

بعد از reload کردن سرویس، هر درخواستی که از ده ثانیه بیشتر طول بکشد با stack trace در آن فایل ثبت می شود.

علت دوم: timeout کوتاه در Nginx

اگر کار واقعا طولانی است، مثلا import محصولات یا خروجی گرفتن از یک گزارش بزرگ، باید مهلت Nginx را بالا ببرید. دو دستور اصلی دارید:

دستورکجا استفاده می شودمقدار پیش فرض
fastcgi_read_timeoutدرخواست های PHP-FPM با fastcgi_pass60s
proxy_read_timeoutدرخواست های proxy_pass به Node، Gunicorn و مانند آن60s
proxy_connect_timeoutبرقراری اتصال به بک اند60s

این مقدارها فقط فاصله بین دو خواندن متوالی از بک اند را محدود می کنند، نه کل مدت درخواست را. مستندات رسمی هر دو را توضیح داده اند: fastcgi_read_timeout و proxy_read_timeout.

برای PHP، داخل بلوک location ~ \.php$ همین سایت اضافه کنید:

location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
    fastcgi_read_timeout 180s;
}

برای بک اندی که با پروکسی وصل است:

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_connect_timeout 10s;
    proxy_read_timeout 180s;
}

مسیر snippets/fastcgi-php.conf و نام فایل socket به توزیع و نسخه PHP بستگی دارند. در CentOS، AlmaLinux و Rocky معمولا include fastcgi_params; و socket یا پورتی مثل 127.0.0.1:9000 می بینید. مقدار را همان جا که قبلا تنظیم شده تغییر دهید و از جای دیگری کپی نکنید.

پیش از ویرایش، از فایل کانفیگ نسخه پشتیبان بگیرید، مثلا با sudo cp /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-available/example.com.conf.bak. بعد از هر تغییر اول sudo nginx -t را اجرا کنید و فقط اگر syntax is ok گرفتید sudo systemctl reload nginx بزنید. reload کانفیگ اشتباه ممکن است سایت را از دسترس خارج کند.

بهتر است timeout بالا را برای کل سایت نگذارید. اگر فقط یک آدرس مثل صفحه import یا خروجی گزارش طولانی است، یک بلوک location جداگانه برای همان آدرس بنویسید و fastcgi_read_timeout را فقط آن جا بالا ببرید. بقیه سایت با مقدار معمولی می ماند و درخواست های معیوب زود تمام می شوند. کارهای دوره ای وردپرس را هم به جای WP-Cron داخل درخواست کاربر، با کران واقعی سرور و WP-CLI اجرا کنید تا بیننده منتظرشان نماند.

علت سوم: تنظیم نبودن PHP در کنار Nginx

بالا بردن fastcgi_read_timeout تنها کافی نیست، چون PHP هم سقف خودش را دارد. اگر max_execution_time در php.ini روی مقدار پیش فرض بماند، اسکریپت قبل از رسیدن به timeout جدید Nginx متوقف می شود و به جای 504 یک صفحه خطا یا خروجی ناقص می گیرید. برای هماهنگی به این سه مقدار نگاه کنید:

  1. max_execution_time در php.ini مربوط به FPM، در Ubuntu معمولا /etc/php/8.2/fpm/php.ini.
  2. request_terminate_timeout در فایل pool. با مقدار 0 غیرفعال است و اگر مقداری بگذارید، PHP-FPM worker را بعد از آن مدت حتی وسط کار می کشد.
  3. fastcgi_read_timeout در Nginx که باید از دو مقدار بالا بیشتر یا مساوی باشد، وگرنه Nginx زودتر از PHP منصرف می شود.

بعد از تغییر php.ini یا فایل pool، سرویس را reload کنید. نام آن بسته به نسخه فرق می کند، مثلا sudo systemctl reload php8.2-fpm. برای مقدارهای مربوط به memory_limit و نسخه PHP در هاست اشتراکی، راهنمای تنظیم نسخه PHP و memory_limit در cPanel مفید است.

علت چهارم: پر شدن workerهای PHP-FPM

گاهی هیچ اسکریپتی کند نیست، ولی تعداد درخواست های هم زمان از pm.max_children بیشتر است. درخواست ها در صف می مانند و بعضی به timeout می رسند. نشانه اش در لاگ PHP-FPM (مثلا /var/log/php8.2-fpm.log) این جمله است:

WARNING: [pool www] server reached pm.max_children setting (5), consider raising it

افزایش pm.max_children باید با RAM سرور هماهنگ باشد. حدود مصرف هر worker را با ps --no-headers -o rss -C php-fpm8.2 ببینید و تعداد را طوری بگذارید که مجموع از حافظه آزاد بیشتر نشود. وگرنه به جای 504 با کشته شدن پردازه ها توسط OOM killer روبه رو می شوید. اگر سایت بدون هیچ بار غیرعادی همیشه به این سقف می رسد، معمولا یا VPS کوچک است یا کش وجود ندارد. چک لیست هاست وردپرس به همین جنبه ها می پردازد.

504 در Cloudflare و تفاوت آن با 524

اگر دامنه پشت Cloudflare است، ممکن است صفحه خطا را خود Cloudflare نشان دهد و کدی مثل 504 یا 524 ببینید. کد 524 مخصوص Cloudflare است و یعنی اتصال به سرور مبدا برقرار شد، ولی پاسخ در مهلت Cloudflare نرسید. این مهلت در پلن های معمولی حدود 100 ثانیه است. بنابراین fastcgi_read_timeout 600s برای درخواستی که از Cloudflare رد می شود فایده ندارد. کار طولانی را باید به صف و پس زمینه ببرید (یک کران، یک worker یا WP-CLI) و فقط وضعیت آن را به کاربر نشان دهید. برای تشخیص سریع، همان آدرس را با IP مستقیم سرور یا از طریق فایل hosts تست کنید. اگر آن جا هم کند است، مشکل از سرور است و Cloudflare تقصیری ندارد.

اگر هاست اشتراکی cPanel دارید

روی هاست اشتراکی به nginx.conf دسترسی ندارید و نباید دنبال آن بروید. دو کار در دسترس شماست. اول، در cPanel از MultiPHP INI Editor مقدار max_execution_time را در حد مجاز پلن بالا ببرید. دوم، افزونه یا اسکریپت کند را با غیرفعال کردن تدریجی پیدا کنید. اگر 504 فقط در ساعت های شلوغ می آید، ممکن است سهم منابع حساب پر شده باشد، که در خطای 508 و محدودیت منابع cPanel توضیح داده شده. اگر هیچ کدام کمک نکرد، با تیکت از پشتیبانی بخواهید لاگ را بررسی کند.

اگر پس از همه این بررسی ها علت پیدا نشد، زمان وقوع خطا را با کارهای زمان بندی شده سرور مقایسه کنید. بکاپ شبانه، بروزرسانی خودکار افزونه ها و ایندکس کردن موتور جست وجو در ساعت های مشخص منابع را می گیرند و درخواست های عادی در همان فاصله به 504 می خورند. دستور crontab -l و فهرست کارهای زمان بندی شده وردپرس این هم زمانی را نشان می دهد.

بعد از رفع، از درست شدنش مطمئن شوید

همان آدرسی را که خطا می داد دوباره باز کنید و همزمان لاگ را زنده ببینید:

sudo tail -f /var/log/nginx/error.log

اگر خط upstream timed out تازه ای ظاهر نشد و صفحه در زمان معقول بالا آمد، مشکل برطرف شده است. اگر بعد از بالا بردن timeout هنوز درخواست های زیادی نزدیک سقف جدید تمام می شوند، دلیلش را در خود اسکریپت جست وجو کنید و عدد را بیشتر نکنید. ماندن در این حالت ظرفیت workerها را اشغال می کند و بقیه درخواست ها را کند می کند.

پیکوهاست VPS در ایران و هلند و فرانسه ارائه می کند که روی آن ها خودتان Nginx و PHP-FPM را پیکربندی می کنید. اگر هنوز بین لوکیشن ها مردد هستید، مقایسه VPS ایران و خارج را بخوانید. برای مشکل بعدی که معمولا پس از تنظیم Nginx برای وردپرس پیش می آید، یعنی 404 پیوندهای یکتا، کانفیگ Nginx برای وردپرس را ببینید.