Introduction

Nginx FastCGI timeout errors occur when the FastCGI backend (typically PHP-FPM) takes longer than the configured fastcgi_read_timeout to respond. This results in Nginx returning a 504 Gateway Timeout error to the client. The issue is common in PHP applications with long-running processes, database queries that exceed expected execution time, or external API calls that introduce latency. Understanding the timeout configuration hierarchy and properly tuning both Nginx and the FastCGI backend is essential for handling long-running requests while maintaining reasonable timeout defaults for normal operations.

Symptoms

When FastCGI timeout occurs, you will observe:

  • HTTP 504 Gateway Timeout error in browser
  • Partial page loads or blank responses
  • Long-running operations failing after exactly 60 seconds (default timeout)
  • Users reporting "page not loading" for specific features

Nginx error log shows:

bash
[error] 12345#12345: *67890 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 192.168.1.100, server: example.com, request: "POST /api/process HTTP/1.1", upstream: "fastcgi://unix:/run/php/php-fpm.sock", host: "example.com"

Or for TCP connections:

bash
[error] upstream timed out (110: Connection timed out) while reading response header from upstream, upstream: "fastcgi://127.0.0.1:9000"

PHP-FPM slow log may show:

bash
[pool www] pid 12345
script_filename = /var/www/html/long-process.php
[0x00007f8b4c000b00] sleep() /var/www/html/long-process.php:45
[0x00007f8b4c000a80] process_data() /var/www/html/long-process.php:30

Common Causes

  1. 1.Default timeout too short - 60 second default insufficient for long-running operations
  2. 2.Slow database queries - Queries taking longer than expected without optimization
  3. 3.External API latency - Third-party API calls with unpredictable response times
  4. 4.PHP memory limit - Script hitting memory limit and garbage collection causing delays
  5. 5.Insufficient PHP-FPM workers - All workers busy, requests queuing
  6. 6.PHP-FPM slowlog not enabled - Difficult to diagnose slow scripts
  7. 7.Network issues - Connectivity problems between Nginx and PHP-FPM
  8. 8.Blocking operations - Synchronous file I/O or external calls blocking execution
  9. 9.Large file processing - Image manipulation, PDF generation, or file uploads
  10. 10.Missing opcache - PHP compilation overhead on each request

Step-by-Step Fix

Step 1: Identify Timeout Source

Check Nginx error logs for timeout details:

```bash # Check recent timeout errors tail -f /var/log/nginx/error.log | grep -i "timed out"

# Find all timeout errors in last hour grep "timed out" /var/log/nginx/error.log | \ awk -v d1="$(date -d '1 hour ago' '+%Y/%m/%d %H')" \ -v d2="$(date '+%Y/%m/%d %H')" \ '$0 > d1 && $0 < d2'

# Count timeouts by endpoint grep "timed out" /var/log/nginx/error.log | \ grep -oP 'request: "\K[^"]+' | \ sort | uniq -c | sort -rn ```

Check which requests are timing out: ``bash # Extract the request paths from timeout errors grep "upstream timed out" /var/log/nginx/error.log | \ sed -n 's/.*request: "\([^"]*\)".*/\1/p' | \ sort | uniq -c | sort -rn | head -20

Step 2: Check Current Timeout Configuration

Review Nginx FastCGI timeout settings:

```bash # Check main configuration grep -r "fastcgi_read_timeout" /etc/nginx/

# Check all timeout-related directives grep -E "(fastcgi_|proxy_|client_|send_|keepalive_|lingeri)" /etc/nginx/nginx.conf grep -rE "(fastcgi_|proxy_|client_|send_|keepalive_)" /etc/nginx/conf.d/ grep -rE "(fastcgi_|proxy_|client_|send_|keepalive_)" /etc/nginx/sites-enabled/ ```

Default timeout values: ``nginx # Default is 60 seconds fastcgi_read_timeout 60s; fastcgi_send_timeout 60s; fastcgi_connect_timeout 60s;

Step 3: Check PHP-FPM Status

Verify PHP-FPM is running and check worker status:

```bash # Check PHP-FPM status systemctl status php8.1-fpm # Adjust version as needed

# Check PHP-FPM pool status via CLI # First enable status path in pool config: # pm.status_path = /status

# Then query status curl -s http://localhost/status?json | jq '.'

# Check worker processes ps aux | grep php-fpm

# Check PHP-FPM slow log location grep "slowlog" /etc/php/8.1/fpm/pool.d/www.conf ```

Review PHP-FPM pool configuration: ``bash cat /etc/php/8.1/fpm/pool.d/www.conf | grep -v "^;" | grep -v "^$"

Key settings to check: ``ini pm = dynamic pm.max_children = 50 pm.start_servers = 5 pm.min_spare_servers = 5 pm.max_spare_servers = 35 pm.max_requests = 500 request_terminate_timeout = 0 ; No hard limit by default

Step 4: Enable PHP-FPM Slow Log

Configure slow log to identify problematic scripts:

bash
# Edit PHP-FPM pool configuration
sudo vim /etc/php/8.1/fpm/pool.d/www.conf

Add or enable these settings: ```ini ; The log file for slow requests slowlog = /var/log/php-fpm-slow.log

; The timeout for serving a single request request_slowlog_timeout = 10s

; Track slow requests even if they don't complete request_slowlog_trace_depth = 20 ```

Restart PHP-FPM to apply: ``bash sudo systemctl restart php8.1-fpm sudo touch /var/log/php-fpm-slow.log sudo chown www-data:www-data /var/log/php-fpm-slow.log

Step 5: Increase FastCGI Timeout

Edit the Nginx configuration to increase timeout:

bash
# Edit site configuration
sudo vim /etc/nginx/sites-available/example.com

Add timeout directives in the FastCGI location block:

```nginx location ~ \.php$ { fastcgi_pass unix:/run/php/php-fpm.sock; 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;

# Optional: Increase buffer sizes for large responses fastcgi_buffer_size 128k; fastcgi_buffers 4 256k; fastcgi_busy_buffers_size 256k; } ```

Apply the configuration: ```bash # Test configuration sudo nginx -t

# Reload Nginx sudo systemctl reload nginx ```

Step 6: Configure Timeout for Specific Endpoints

Set different timeouts for different endpoints:

```nginx # Normal requests - default timeout location ~ \.php$ { fastcgi_pass unix:/run/php/php-fpm.sock; fastcgi_read_timeout 60s; include fastcgi_params; }

# Long-running API endpoints location ~ ^/api/(import|export|report) { fastcgi_pass unix:/run/php/php-fpm.sock; fastcgi_read_timeout 600s; # 10 minutes include fastcgi_params; }

# Background processing endpoints location ~ ^/background/ { fastcgi_pass unix:/run/php/php-fpm.sock; fastcgi_read_timeout 1800s; # 30 minutes include fastcgi_params; } ```

Step 7: Optimize PHP-FPM Workers

Adjust PHP-FPM pool settings for better concurrency:

bash
sudo vim /etc/php/8.1/fpm/pool.d/www.conf

Optimize settings based on server resources:

```ini ; Calculate max_children based on available RAM ; Formula: max_children = (Total RAM - RAM for other services) / Average PHP process size ; Example: (8GB - 2GB) / 100MB = 60

pm = dynamic pm.max_children = 50 pm.start_servers = 10 pm.min_spare_servers = 5 pm.max_spare_servers = 20 pm.max_requests = 1000

; Set hard timeout limit (0 = unlimited, but set a safety limit) request_terminate_timeout = 600s ```

Restart PHP-FPM: ``bash sudo systemctl restart php8.1-fpm

Step 8: Optimize Slow PHP Code

Based on slow log findings, optimize problematic code:

bash
# Review slow log entries
tail -100 /var/log/php-fpm-slow.log

Common patterns and fixes:

```php // BEFORE: Slow database query $users = DB::select("SELECT * FROM users WHERE status = 1"); foreach ($users as $user) { $orders = DB::select("SELECT * FROM orders WHERE user_id = ?", [$user->id]); }

// AFTER: Eager loading $users = DB::select(" SELECT u.*, GROUP_CONCAT(o.id) as order_ids FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE u.status = 1 GROUP BY u.id ");

// BEFORE: Blocking API call $response = file_get_contents('https://slow-api.example.com/data');

// AFTER: Set timeout $context = stream_context_create([ 'http' => ['timeout' => 30] ]); $response = file_get_contents('https://slow-api.example.com/data', false, $context);

// Process large datasets in chunks $chunkSize = 1000; $offset = 0; do { $batch = DB::select("SELECT * FROM large_table LIMIT ? OFFSET ?", [$chunkSize, $offset]); foreach ($batch as $record) { processRecord($record); } $offset += $chunkSize; // Flush output to prevent timeout if (connection_status() == CONNECTION_NORMAL) { echo "Processed $offset records...\n"; flush(); } } while (count($batch) == $chunkSize); ```

Verification

After making changes, verify the configuration is working:

```bash # Test Nginx configuration sudo nginx -t

# Check Nginx status sudo systemctl status nginx

# Check PHP-FPM status sudo systemctl status php8.1-fpm

# Test with a long-running request curl -X POST -H "Content-Type: application/json" \ -d '{"action": "long-process"}' \ -w "Time: %{time_total}s\nStatus: %{http_code}\n" \ http://localhost/api/long-process ```

Monitor logs for timeout errors: ```bash # Watch for new timeout errors tail -f /var/log/nginx/error.log | grep --line-buffered "timed out"

# Count timeouts per minute watch -n 60 'grep "timed out" /var/log/nginx/error.log | grep "$(date +%Y/%m/%d\ %H:%M)" | wc -l' ```

Verify PHP-FPM metrics: ```bash # Check current connections curl -s http://localhost/status | grep -E "(idle|active|total)"

# Expected output: # idle processes: 10 # active processes: 5 # total processes: 15 ```

Prevention

To prevent future FastCGI timeout issues:

  1. 1.Set appropriate timeouts per endpoint:
  2. 2.```nginx
  3. 3.# Map timeout based on request path
  4. 4.map $request_uri $fastcgi_timeout {
  5. 5.default 60s;
  6. 6.~^/api/import 600s;
  7. 7.~^/api/export 600s;
  8. 8.~^/api/report 300s;
  9. 9.}

location ~ \.php$ { fastcgi_read_timeout $fastcgi_timeout; } ```

  1. 1.Implement background job processing:
  2. 2.```php
  3. 3.// Instead of processing in request, queue for background
  4. 4.Queue::push(new ProcessLargeDatasetJob($datasetId));
  5. 5.return response()->json(['job_id' => $jobId]);
  6. 6.`
  7. 7.Add progress reporting for long operations:
  8. 8.```php
  9. 9.// Stream progress updates
  10. 10.header('Content-Type: text/event-stream');
  11. 11.header('Cache-Control: no-cache');
  12. 12.header('Connection: keep-alive');

foreach ($items as $i => $item) { processItem($item); echo "data: " . json_encode(['progress' => ($i + 1) / count($items) * 100]) . "\n\n"; flush(); } ```

  1. 1.Monitor and alert on timeouts:
  2. 2.```bash
  3. 3.# Add monitoring for timeout rate
  4. 4.grep -c "timed out" /var/log/nginx/error.log
  5. 5.`
  6. 6.Use PHP-FPM status endpoint monitoring:
  7. 7.```bash
  8. 8.# Monitor worker utilization
  9. 9.curl -s http://localhost/status?json | jq '.idle + .active'
  10. 10.`
  11. 11.Implement circuit breakers for external APIs:
  12. 12.```php
  13. 13.// Use timeout and retry logic
  14. 14.$client = new GuzzleHttp\Client(['timeout' => 30]);
  15. 15.try {
  16. 16.$response = $client->get('https://external-api.example.com/data');
  17. 17.} catch (ConnectException $e) {
  18. 18.// Handle timeout gracefully
  19. 19.return cachedResponse();
  20. 20.}
  21. 21.`
  22. 22.Regular slow log review:
  23. 23.```bash
  24. 24.# Weekly slow log analysis cron job
  25. 25.0 0 * * 0 cat /var/log/php-fpm-slow.log | mail -s "Weekly PHP Slow Log" admin@example.com
  26. 26.`
  • [Nginx troubleshooting: Fix Lambda Permission Denied - Complete ](fix-lambda-permission-denied)
  • [Nginx web server troubleshooting: Fix Client Max Body Size Large Upload Nginx Issue ](client-max-body-size-large-upload-nginx)
  • [Fix Apache 502 Proxy Error](fix-apache-502-proxy-error)
  • [Fix Apache LogLevel Core Debug Configuration](fix-apache-loglevel-core-debug)
  • [Fix Cloudflare 502 Bad Gateway Error](fix-cloudflare-502-bad-gateway)

<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "Nginx FastCGI Timeout", "description": "Fix Nginx FastCGI timeout errors by adjusting fastcgi_read_timeout, optimizing PHP-FPM, and diagnosing slow backend processes.", "url": "https://www.fixwikihub.com/fix-nginx-fastcgi-timeout", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2025-12-12T22:23:23.984Z", "dateModified": "2025-12-12T22:23:23.984Z" } </script>