Introduction

The Nginx "upstream timed out" error occurs when Nginx acts as a reverse proxy to PHP-FPM via FastCGI and the backend fails to respond within the configured timeout window. This manifests as a 504 Gateway Timeout in the browser, and the Nginx error log records a message like:

bash
2026/04/08 14:23:01 [error] 1234#0: *5678 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 192.168.1.50, server: example.com, request: "POST /api/import HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "example.com"

Symptoms

  • Browser displays 504 Gateway Timeout after approximately 60 seconds
  • Nginx error log shows "upstream timed out (110: Connection timed out) while reading response header from upstream"
  • PHP-FPM process may still be running the script even after Nginx returns the error
  • Slow-running scripts (database imports, report generation) consistently trigger the timeout
  • The error appears only for specific long-running endpoints, not site-wide

Common Causes

  • fastcgi_read_timeout default value of 60s is too short for long-running PHP scripts
  • PHP max_execution_time set higher than Nginx's FastCGI timeout, causing PHP to keep working while Nginx has already timed out
  • PHP-FPM pool request_terminate_timeout killing the process before script completion
  • Database queries or external API calls within PHP taking longer than expected
  • Insufficient PHP-FPM child processes causing request queuing

Step-by-Step Fix

  1. 1.Increase FastCGI read timeout in Nginx server block:
  2. 2.```nginx
  3. 3.server {
  4. 4.listen 80;
  5. 5.server_name example.com;
  6. 6.root /var/www/html;
  7. 7.index index.php;

location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params;

# Increase timeout for long-running requests fastcgi_read_timeout 300s; fastcgi_send_timeout 300s; fastcgi_connect_timeout 60s; fastcgi_buffer_size 128k; fastcgi_buffers 4 256k; fastcgi_busy_buffers_size 256k; } } ```

  1. 1.Align PHP max_execution_time with Nginx timeout. Edit /etc/php/8.2/fpm/php.ini:
  2. 2.```ini
  3. 3.max_execution_time = 300
  4. 4.`
  5. 5.This ensures PHP does not terminate before Nginx times out, or vice versa.
  6. 6.Verify PHP-FPM pool settings in /etc/php/8.2/fpm/pool.d/www.conf:
  7. 7.```ini
  8. 8.request_terminate_timeout = 300s
  9. 9.`
  10. 10.This setting overrides max_execution_time in PHP-FPM and must also be increased.
  11. 11.Test and reload configurations:
  12. 12.```bash
  13. 13.sudo nginx -t
  14. 14.sudo systemctl reload nginx
  15. 15.sudo systemctl reload php8.2-fpm
  16. 16.`
  17. 17.Verify the fix by monitoring the Nginx error log during a long-running request:
  18. 18.```bash
  19. 19.sudo tail -f /var/log/nginx/error.log | grep "upstream timed out"
  20. 20.`
  21. 21.If no timeout messages appear during the previously failing request, the fix is successful.

Prevention

  • Monitor PHP-FPM slow log by enabling it: slowlog = /var/log/php-fpm/slow.log and request_slowlog_timeout = 5s in the pool configuration
  • Set up alerting on 504 response rates using Nginx access log parsing
  • Profile long-running PHP scripts with Xdebug or Blackfire to identify optimization opportunities
  • Offload batch processing to background job queues (e.g., Laravel Horizon, Symfony Messenger) instead of HTTP requests
  • Use fastcgi_next_upstream timeout if you have multiple PHP-FPM backends for automatic failover