# 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:
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.PHP-FPM max_children exhausted -- All worker processes are busy, and new requests queue until they time out
- 2.Long-running PHP script -- A report generation, data export, or batch job exceeds the timeout
- 3.PHP-FPM pool misconfiguration -- The process manager cannot scale fast enough for traffic spikes
- 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:
pm.status_path = /fpm-statusThen query it:
curl -s http://127.0.0.1/fpm-statusLook for these key metrics:
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: 847The 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:
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:
pm.max_children = 60
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15
pm.max_requests = 500Set 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:
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:
; In /etc/php/8.2/fpm/pool.d/www.conf
request_slowlog_timeout = 5s
slowlog = /var/log/php8.2-fpm-slow.logAfter reload, the slow log will show exact PHP stack traces for requests exceeding 5 seconds:
[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:34This reveals exactly which code path is slow, allowing targeted optimization rather than blindly increasing timeouts.
Step 5: Reload Services
Apply changes without dropping connections:
sudo systemctl reload php8.2-fpm
sudo systemctl reload nginxMonitor the error log for 10 minutes after reload:
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 reachedandlisten queuemetrics - 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.