# Fix Nginx 504 Gateway Timeout: Upstream Timed Out Connecting to PHP-FPM

If you have ever stared at a 504 Gateway Timeout in production and found this in your Nginx error log, you know the frustration:

bash
2026/04/08 14:23:07 [error] 1234#1234: *56789 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 203.0.113.42, server: api.example.com, request: "POST /api/v2/reports/generate HTTP/1.1", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock:", host: "api.example.com"

This error means Nginx accepted the client request, forwarded it to PHP-FPM via FastCGI, and waited for a response -- but PHP-FPM never replied within the configured timeout window. The connection is not refused; it is simply silent.

Why This Happens

The most common causes in production are:

  1. 1.PHP-FPM max_children exhausted -- All worker processes are busy, and new requests queue until they time out
  2. 2.Long-running PHP script -- A report generation, data export, or batch job exceeds the timeout
  3. 3.PHP-FPM pool misconfiguration -- The process manager cannot scale fast enough for traffic spikes
  4. 4.Database lock or slow query -- PHP waits on the database, which waits on locks, creating a cascade

Step 1: Check PHP-FPM Status

First, verify whether PHP-FPM is hitting its process limits. Enable the status page in your pool configuration at /etc/php/8.2/fpm/pool.d/www.conf:

bash
pm.status_path = /fpm-status

Then query it:

bash
curl -s http://127.0.0.1/fpm-status

Look for these key metrics:

bash
pool:                 www
process manager:      dynamic
start time:           08/Apr/2026:06:00:00 +0000
start since:          30245
accepted conn:        148723
listen queue:         23
max listen queue:     156
listen queue len:     511
idle processes:       0
active processes:     50
max active processes: 50
max children reached: 847

The critical number is max children reached. If this is greater than zero, PHP-FPM has hit its ceiling and is rejecting or queuing new requests. The listen queue value shows how many requests are currently waiting.

Step 2: Tune PHP-FPM Pool Settings

If max children reached is non-zero, increase pm.max_children. Calculate an appropriate value:

bash
ps --no-headers -o "rss,cmd" -C php-fpm8.2 | awk '{sum+=$1} END {printf "%dMB\n", sum/NR/1024}'

If each process uses roughly 64MB and you have 4GB available for PHP-FPM:

bash
pm.max_children = 60
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15
pm.max_requests = 500

Set pm.max_requests to recycle workers periodically, preventing memory leaks from accumulating over thousands of requests.

Step 3: Adjust Nginx FastCGI Timeouts

In your Nginx server block or fastcgi.conf, increase the timeout values:

nginx
location ~ \.php$ {
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
    fastcgi_read_timeout 120s;
    fastcgi_send_timeout 120s;
    fastcgi_connect_timeout 30s;
    fastcgi_buffer_size 128k;
    fastcgi_buffers 4 256k;
    fastcgi_busy_buffers_size 256k;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

The fastcgi_read_timeout controls how long Nginx waits for PHP-FPM to send response data. Default is 60 seconds; increase it if your application legitimately runs long operations.

Step 4: Enable PHP-FPM Slow Log

To identify which specific scripts are causing delays, enable the slow log:

bash
; In /etc/php/8.2/fpm/pool.d/www.conf
request_slowlog_timeout = 5s
slowlog = /var/log/php8.2-fpm-slow.log

After reload, the slow log will show exact PHP stack traces for requests exceeding 5 seconds:

bash
[08-Apr-2026 14:23:05]  [pool www] pid 2345
script_filename = /var/www/app/public/index.php
[0x00007f3c2a1b4560] curl_exec() /var/www/app/src/Services/ExternalApiService.php:142
[0x00007f3c2a1b4320] fetchReportData() /var/www/app/src/Controllers/ReportController.php:89
[0x00007f3c2a1b4100] generate() /var/www/app/src/Controllers/ReportController.php:34

This reveals exactly which code path is slow, allowing targeted optimization rather than blindly increasing timeouts.

Step 5: Reload Services

Apply changes without dropping connections:

bash
sudo systemctl reload php8.2-fpm
sudo systemctl reload nginx

Monitor the error log for 10 minutes after reload:

bash
sudo tail -f /var/log/nginx/error.log | grep "upstream timed out"

If the messages stop and the PHP-FPM status shows listen queue: 0, the fix is working.

Prevention

  • Set up monitoring on max children reached and listen queue metrics
  • Use APM tools to track slow database queries that cascade into PHP-FPM exhaustion
  • Implement request queuing or background job processing for long-running operations instead of synchronous HTTP requests
  • Add health check endpoints that verify PHP-FPM can reach the database before accepting production traffic

This pattern of upstream timeout is one of the most common production issues in Nginx and PHP-FPM deployments. The key is understanding whether the bottleneck is in PHP-FPM capacity, application code performance, or downstream services -- and the slow log combined with the status page will tell you exactly which it is.