Fix 504 Gateway Timeout Error in Nginx

By 

•

Published on

•

10 min read

Nginx emblem connected to a 504 response card with an amber stopwatch on a sage background

Your site loads normally until one report, upload, API call, or admin page takes too long and Nginx returns 504 Gateway Time-out. Nginx is answering the request, but an operation involving the service behind it, called the upstream, has timed out.

Increasing a timeout can give a legitimate slow operation enough time to finish, but it can also hide a stuck application or database query. This guide starts with the logs and upstream response time, then shows which Nginx directive to change after you know where the delay occurs.

You need access to the server and a user with sudo privileges. The examples use Ubuntu and Debian configuration paths and were tested with Nginx 1.28.3 and PHP-FPM 8.5 on Ubuntu 26.04. Other installations often keep site configurations under /etc/nginx/conf.d/ instead of /etc/nginx/sites-available/.

If you are visiting someone else’s website, try reloading the page or returning later. The checks below are for the person managing the server.

Quick Reference

For a printable quick reference, see the Nginx cheatsheet .

TaskCommand or directive
Follow the Nginx error logsudo tail -f /var/log/nginx/error.log
Check the Nginx servicesudo systemctl status nginx
Time a local HTTP upstreamtime curl --max-time 130 -v http://127.0.0.1:3000/health
Read application service logssudo journalctl -u myapp -n 100
Extend an HTTP upstream timeoutproxy_read_timeout 120s;
Extend a PHP-FPM timeoutfastcgi_read_timeout 120s;
Test the Nginx configurationsudo nginx -t
Apply a valid configurationsudo systemctl reload nginx

What a 504 Error Means

Nginx acts as a gateway when it sends a request to an upstream such as a Node.js application, a Python application server, PHP-FPM, or another proxy. A 504 response means that an upstream operation, such as connecting or waiting for a response, exceeded its timeout before Nginx could send response headers to the client.

This differs from a 502 Bad Gateway response . A 502 commonly means that the upstream refused the connection or returned an invalid response. If the error log shows a refused connection or a missing socket, you need the 502 checks. A longer read timeout will not fix it.

If Nginx has already sent response headers, it cannot replace that response with a 504 page. A later upstream timeout can leave the client with a truncated download or a broken stream. The access log still records the original status, such as 200. The error log identifies the timeout in either case.

The browser may show an Nginx page even when another gateway sits in front of it. A CDN, cloud load balancer, or second reverse proxy can have its own timeout, so identify which layer generated the response before changing Nginx.

Read the Nginx Error Log

Reproduce the failing request while following the error log:

Terminal
sudo tail -f /var/log/nginx/error.log

A typical HTTP upstream timeout looks like this:

output
upstream timed out (110: Connection timed out) while reading response header from upstream, client: 203.0.113.20, server: example.com, request: "GET /reports/monthly HTTP/1.1", upstream: "http://127.0.0.1:3000/reports/monthly"

The line identifies the request, the upstream address, and the stage that timed out. while reading response header from upstream means Nginx connected to the application but did not receive its response headers in time. A connection timeout points to reachability or an overloaded listener instead.

Use the stage in the message to choose the next check:

Log messageWhat to check
while connecting to upstreamUpstream address, listener, routing, and firewall; proxy_connect_timeout for HTTP or fastcgi_connect_timeout for PHP-FPM
while sending request to upstreamWhether the upstream is accepting request data; proxy_send_timeout or fastcgi_send_timeout
while reading response header from upstreamApplication workers, database queries, and response delays; proxy_read_timeout or fastcgi_read_timeout
while reading upstreamA response body that stopped arriving after headers; inspect stalled downloads or streams

Match the timestamp and request path against the application log. If the backend is managed by systemd, replace myapp with its unit name:

Terminal
sudo journalctl -u myapp -n 100

The Nginx log tells you where the wait occurred; the application log usually tells you what the request was doing during that wait. Our Nginx log guide covers custom log locations and formats.

Test the Upstream Directly

Bypass Nginx and time the same upstream from the web server. For an application listening on port 3000, start with its health endpoint:

Terminal
time curl --max-time 130 -v http://127.0.0.1:3000/health

Then test the path that triggers the 504 when it is safe to repeat:

Terminal
time curl --max-time 130 -v http://127.0.0.1:3000/reports/monthly

Replace the address, port, and path with the values from your configuration. The --max-time 130 option caps the entire diagnostic request at 130 seconds, including connection time. It gives our 120-second timeout example room to complete without leaving curl waiting indefinitely. A curl timeout is a client-side limit, not proof that Nginx generated a 504.

If the direct request is also slow, the problem is in the application, and Nginx is only reporting it. Check for a slow database query, an exhausted worker pool, a blocked external API request, CPU saturation, memory pressure, or disk I/O. Restarting Nginx will not make that work complete faster.

If the local request responds quickly while the public request fails, check whether Nginx uses the same upstream address, path, and Host header. Our Nginx reverse proxy guide explains how proxy_pass and forwarded headers affect the request. Also inspect any load balancer or CDN in front of Nginx.

You cannot run these HTTP checks against a PHP-FPM socket. PHP-FPM speaks FastCGI, not HTTP. For PHP requests, check the FPM service and logs instead.

Fix the Slow Application First

Measure the request before you raise a timeout. A report that reliably finishes in 70 seconds may need a larger allowance. An ordinary page that suddenly waits for several minutes needs an application fix.

Useful checks include:

  • Confirm that all expected application workers are running and not stuck.
  • Inspect slow-query logs and database connection-pool usage.
  • Check CPU, memory, disk I/O, and out-of-memory events at the failure time.
  • Add timeouts to outbound API calls so a third-party service cannot hold a worker forever.
  • Move exports, video processing, imports, and other long jobs to a background queue, then return a job ID to the browser.

The last option is usually the right design for work that may take minutes. A web request is a poor job supervisor, and every open request occupies resources in Nginx, the application, and often the database.

Increase proxy_read_timeout for HTTP Upstreams

When the application is healthy and the request legitimately needs more than the default 60 seconds between reads, set proxy_read_timeout in the location that uses proxy_pass:

/etc/nginx/sites-available/example.comnginx
location /reports/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_read_timeout 120s;
}

The proxy_read_timeout directive measures inactivity between upstream reads, not the total response duration. A stream can run longer than the setting as long as data keeps arriving. If it stalls after headers reach the client, Nginx closes the connection, and you get a truncated response with no 504 page.

Keep the setting in the narrowest applicable location block. Raising it globally makes every stalled request occupy a connection longer and can delay detection of a failed backend.

Test the configuration before reloading Nginx:

Terminal
sudo nginx -t
sudo systemctl reload nginx

Run the reload only if the configuration test succeeds. The reload applies the new timeout, and existing connections finish on the old workers. Repeat the failing request and check the error log again.

Increase fastcgi_read_timeout for PHP-FPM

PHP-FPM sites use FastCGI, not proxy_pass, so proxy_read_timeout does not affect them. Add fastcgi_read_timeout to the existing PHP location instead, keeping your site’s document root and other PHP settings:

/etc/nginx/sites-available/example.comnginx
location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.5-fpm.sock;
    fastcgi_read_timeout 120s;
}

The socket above is for PHP 8.5 on Ubuntu 26.04. Keep the socket or TCP address your FPM pool actually uses. Like the HTTP read timeout, fastcgi_read_timeout defaults to 60 seconds and limits inactivity between reads.

Test and reload Nginx after saving the change, running the second command only if the test succeeds:

Terminal
sudo nginx -t
sudo systemctl reload nginx

Read the FPM service journal and log for the same request time:

Terminal
sudo journalctl -u php8.5-fpm -n 100
sudo tail -n 50 /var/log/php8.5-fpm.log

Replace the version in these paths if you run a different PHP release. Look for worker termination, script errors, or server reached pm.max_children. That warning means the pool has no free workers. Check memory use and slow requests before raising the worker limit.

PHP can stop a script before Nginx reaches its own timeout. Check max_execution_time in the FPM php.ini, such as /etc/php/8.5/fpm/php.ini, and request_terminate_timeout in the active pool file under /etc/php/8.5/fpm/pool.d/. On Linux, max_execution_time counts only the time PHP spends running the script. Waiting on a database query, a remote API, or sleep() does not count, so a script blocked on a slow query reaches the Nginx timeout first. The CLI configuration can differ. The FPM pool timeout kills a worker that exceeds its limit. If the connection closes before response headers arrive, Nginx returns a 502. Do not raise all the limits blindly; use the log to find which one was reached.

Check Timeouts in Every Proxy Layer

A request can pass through a CDN, load balancer, Nginx, and the application server before reaching your code. Each layer can end the request independently, and the limits may measure different things: connection time, inactivity, or total request duration. If the upstream completes successfully but the browser still receives a 504, inspect the gateway in front of Nginx.

Record the exact failure duration. A response that fails consistently at 30 or 60 seconds often points to a configured limit. Compare that duration with the timeout on each layer instead of increasing every value at once.

Troubleshooting

The error remains after raising proxy_read_timeout
Inspect the effective configuration with sudo nginx -T, find the server and location handling the request, and confirm that you reloaded after the edit. The output can contain sensitive configuration, so do not post it publicly without reviewing it. Then check whether another gateway returns the error first. If the application never finishes, a larger Nginx value only postpones the failure.

Nginx reports while connecting to upstream
Check the address, port, DNS resolution, firewall, and upstream listener. proxy_connect_timeout controls connection establishment, but a healthy service on the same host should connect almost immediately. Treat a slow connection as a service or network problem first.

Only PHP requests time out
Use fastcgi_read_timeout and inspect PHP-FPM for exhausted workers or a terminated script. The active pool and php.ini may differ between the command line and FPM.

The 504 appears only during traffic spikes
Check application worker queues, database connection limits, and CPU and memory pressure. If requests wait for a free worker, increasing the timeout accepts a longer queue but does not add capacity.

The error page names Cloudflare or another provider
Cloudflare can pass through an origin error or generate its own; its 502/504 troubleshooting guide explains the distinction. Test the origin while preserving the hostname and HTTPS server name, for example with curl’s --resolve option, and keep certificate checks and origin access restrictions in place. When Cloudflare gives up waiting for your origin, it shows its own 524 error, not a 504. Its default limit is 125 seconds. Increasing the Nginx timeout cannot override a provider’s limit.

Conclusion

For work measured in minutes, move the job out of the request path and let the browser check its progress. The HTTP status codes cheatsheet compares 504 with other gateway and server errors.

Tags

Linuxize Weekly Newsletter

A quick weekly roundup of new tutorials, news, and tips.

About the authors

Dejan Panovski

Dejan Panovski

Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 1000+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.

View author page