sz

Sejan

Blog

How to Fix Nginx 504 Gateway Time-out & 502 Bad Gateway with PHP-FPM

Home › DevOps & Cloud › How to Fix Nginx 504 Gateway Time-out & 502 Bad Gateway with PHP-FPM
How to Fix Nginx 504 Gateway Time-out & 502 Bad Gateway with PHP-FPM

One of the most frequent performance headaches for DevOps engineers and system administrators is encountering HTTP 504 Gateway Time-out and HTTP 502 Bad Gateway errors on high-traffic Nginx web servers running PHP-FPM.

When an application processes a demanding request—such as a large PDF generation, bulk data export, third-party API integration, or intensive SQL query—Nginx waits for PHP-FPM to respond. If PHP-FPM takes longer than the configured threshold or exhausts its process pool, Nginx gives up and serves a 504 Gateway Time-out to the visitor.

In this guide, we will dissect the underlying cause, check the exact error logs, and walk through the configuration adjustments needed in both Nginx and PHP-FPM to resolve and prevent gateway timeouts permanently.

Understanding the Architecture: Nginx + FastCGI

Nginx does not process PHP code directly. Instead, it acts as a high-performance reverse proxy that forwards dynamic requests over a Unix socket or TCP port to the PHP FastCGI Process Manager (PHP-FPM).

The 504 vs 502 Difference:
• 504 Gateway Time-out: Nginx connected to PHP-FPM, but PHP-FPM did not finish executing and return a response within Nginx's fastcgi_read_timeout.
• 502 Bad Gateway: PHP-FPM crashed, closed the socket connection prematurely (often due to hitting PHP memory limits or a segfault), or all workers were occupied and rejected the incoming socket connection.

Step 1: Inspecting the Error Logs

Before modifying any configuration files, check your server error logs to confirm whether the bottleneck is timeout-related or memory/pool-related:

# Check Nginx error log sudo tail -n 50 /var/log/nginx/error.log # Common Nginx timeout signature: # [error] 1420#1420: *31 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 203.0.113.19, server: sejan.dev, request: "POST /reports/export HTTP/2.0", upstream: "fastcgi://unix:/run/php/php8.3-fpm.sock:"

Next, check the PHP-FPM log:

# Check PHP-FPM error log sudo tail -n 50 /var/log/php8.3-fpm.log # Common PHP-FPM pool exhaustion warning: # [WARNING] [pool www] server reached pm.max_children setting (10), consider raising it

Step 2: Tune Nginx FastCGI Timeouts & Buffers

Open your Nginx server block configuration (typically located in /etc/nginx/sites-available/your-domain.conf or /etc/nginx/nginx.conf):

sudo nano /etc/nginx/sites-available/your-domain.conf

Locate the location ~ \.php$ block and add or increase the timeout and buffer directives:

server {    listen 443 ssl http2;    server_name sejan.dev;    root /var/www/html/public;    index index.php index.html;    location / {        try_files $uri $uri/ /index.php?$query_string;    }    location ~ \.php$ {        include snippets/fastcgi-php.conf;        fastcgi_pass unix:/run/php/php8.3-fpm.sock;        # ==========================================        # FastCGI Timeout Configuration (Default is 60s)        # ==========================================        fastcgi_connect_timeout 60s;        fastcgi_send_timeout 300s;        fastcgi_read_timeout 300s;        # ==========================================        # FastCGI Buffer Tuning (Prevents disk buffering & 502/504 errors)        # ==========================================        fastcgi_buffer_size 128k;        fastcgi_buffers 4 256k;        fastcgi_busy_buffers_size 256k;        fastcgi_temp_file_write_size 256k;    } }

Step 3: Adjust PHP-FPM Execution Limits

Increasing Nginx timeouts won't help if PHP itself terminates the script after 30 seconds. You must align PHP's execution limits with Nginx.

1. Update php.ini

Edit your active CLI/FPM php.ini (e.g., /etc/php/8.3/fpm/php.ini):

# Find and adjust these settings max_execution_time = 300 max_input_time = 300 memory_limit = 256M

2. Update PHP-FPM Pool Configuration

Edit the pool config file (e.g., /etc/php/8.3/fpm/pool.d/www.conf):

sudo nano /etc/php/8.3/fpm/pool.d/www.conf

Set request_terminate_timeout to match your Nginx timeout:

; Terminate requests that run longer than 300 seconds request_terminate_timeout = 300s

Step 4: Scale PHP-FPM Worker Pool (Prevent 502/504 Under Load)

If your server experiences timeouts because all worker processes are busy handling concurrent requests, increase the pool capacity according to your server's available RAM:

; Pool Process Management pm = dynamic pm.max_children = 50 pm.start_servers = 10 pm.min_spare_servers = 5 pm.max_spare_servers = 20 pm.max_requests = 500

DevOps Tip — How to Calculate pm.max_children:
Average PHP-FPM process memory usage is usually ~30MB–60MB. If your server has 4GB of RAM and you allocate 2.5GB to PHP-FPM:
2500 MB / 50 MB per process = 50 max_children

Step 5: Validate and Reload Services

Always test syntax before reloading production daemons to avoid downtime:

# 1. Test Nginx syntax sudo nginx -t # 2. Test PHP-FPM syntax sudo php-fpm8.3 -t # 3. Gracefully reload both services sudo systemctl reload php8.3-fpm sudo systemctl reload nginx

Architecture Best Practice: Offload to Background Queues

While increasing HTTP timeouts solves acute timeout errors, long-running web requests consume PHP workers and degrade user experience. For robust application architecture:

  • Use Asynchronous Queues: Offload report generation, heavy data syncing, and emails to background workers (e.g., Laravel Queues, Redis, RabbitMQ, or Celery).
  • Return Instant HTTP 202: Acknowledge the request immediately with an HTTP 202 Accepted status and notify the user via WebSockets or polling when the job finishes.

Summary

Eliminating Nginx 504 and 502 errors comes down to synchronizing three key layers:

  1. Nginx fastcgi_read_timeout (e.g., 300s).
  2. PHP max_execution_time and request_terminate_timeout (e.g., 300s).
  3. Right-sizing pm.max_children so your server never runs out of available PHP workers.

Comments (0)

Leave a Reply ↓

No comments yet. Be the first to start the discussion!

Leave a Comment