How to Fix Nginx 504 Gateway Timeout Errors

Last updated Sunday, May 17, 2026 4:35 pm Joshua James 12 min read

An Nginx 504 Gateway Timeout error means Nginx waited too long for the next upstream step to complete. The upstream might be a reverse-proxied application, a PHP-FPM pool, a container, an API server, a private backend, a load balancer, or another proxy layer in front of your origin.

The fix is not always a larger timeout. A 504 can come from a slow application, overloaded workers, a dead backend, a DNS or network path problem, PHP-FPM execution limits, a container mapping issue, or an edge proxy that timed out before the request reached your Nginx server. Start by proving where the wait happens, then change only the setting that owns that layer.

Keep the checks distro-neutral: identify whether the 504 belongs to a reverse proxy backend, PHP-FPM, an upstream network, a container, a CDN or load-balancer layer, or a route that genuinely needs more time before changing configuration.

Understand Nginx 504 Gateway Timeout Causes

A 504 error is a timeout while Nginx is acting as a gateway or proxy. Nginx may be waiting to connect to the upstream, send the request to it, or read the response from it. The exact stage usually appears in the Nginx error log.

SymptomLikely CauseFirst Check
upstream timed out while reading response headerThe backend accepted the request but did not start sending a response in time.Check application logs, worker load, and proxy_read_timeout or fastcgi_read_timeout.
upstream timed out while connecting to upstreamNginx could not establish the upstream connection before the connect timeout.Test the upstream address and port directly from the Nginx host.
upstream timed out while sending request to upstreamThe upstream was too slow to receive the request body or Nginx could not send data quickly enough.Check upload size, backend load, request body handling, and proxy_send_timeout.
504 appears only for PHP pagesPHP-FPM or the application code is taking longer than Nginx or PHP-FPM allows.Check PHP-FPM logs, fastcgi_read_timeout, PHP execution limits, and pool capacity.
504 appears only through a CDN or load balancerThe edge layer timed out before or while talking to the origin.Compare edge requests with a direct origin request using the same host header.
504 appears during large reports, imports, or admin tasksThe request is synchronous and takes too long for a web request path.Move long work to a background job when possible, or tune timeouts for that one route.

The directives that usually matter are timeout controls for the specific upstream type. Keep them scoped to the affected server or location block unless the whole site genuinely needs the same behavior.

DirectiveContextUse in 504 Diagnosis
proxy_connect_timeouthttp, server, locationControls how long Nginx waits to establish a connection to a proxied HTTP upstream.
proxy_send_timeouthttp, server, locationControls the timeout between successive write operations while sending a request to the upstream.
proxy_read_timeouthttp, server, locationControls the timeout between successive read operations while receiving the upstream response.
fastcgi_connect_timeouthttp, server, locationControls how long Nginx waits to connect to a FastCGI service such as PHP-FPM.
fastcgi_send_timeouthttp, server, locationControls the timeout between successive write operations while sending a request to the FastCGI service.
fastcgi_read_timeouthttp, server, locationControls the timeout between successive read operations from a FastCGI service such as PHP-FPM.
uwsgi_read_timeouthttp, server, locationApplies to uWSGI upstreams when the site uses uwsgi_pass instead of proxy_pass or fastcgi_pass.
error_logmain, http, server, locationShows which timeout stage failed and which upstream address or socket was involved.

Do not raise every timeout globally as the first fix. A higher timeout can hide a crashed worker, a saturated application pool, a broken upstream address, or a request that should run as a background job instead of an HTTP request.

Diagnose an Nginx 504 Gateway Timeout

First confirm that the response is a 504 and identify which layer returned it. Use the curl command from a client machine or from the server itself:

curl -I http://example.com

A response generated by Nginx often looks like this:

HTTP/1.1 504 Gateway Timeout
Server: nginx

If the server header names a CDN, load balancer, reverse proxy manager, or hosting platform, keep that layer in scope. The origin Nginx logs may show no matching request when the timeout happened before the request reached the origin.

Check the Nginx Error Log for 504 Details

The Nginx error log is the fastest way to separate a slow backend from a connection problem. Use the tail command against the common packaged log path first:

sudo tail -n 50 /var/log/nginx/error.log

Relevant timeout fragments often look like one of these lines:

upstream timed out (110: Connection timed out) while connecting to upstream
upstream timed out (110: Connection timed out) while sending request to upstream
upstream timed out (110: Connection timed out) while reading response header from upstream

When the site uses per-vhost logs or a custom layout, pipe the parsed configuration through the grep command to discover the active log directives:

sudo nginx -T 2>/dev/null | grep -E '^[[:space:]]*(access_log|error_log)'

Relevant lines can show the log file that owns the affected virtual host:

error_log /var/log/nginx/example.error.log warn;
access_log /var/log/nginx/example.access.log main;

The Nginx access and error logs guide covers log path discovery, access log filtering, and custom upstream timing fields in more depth.

Confirm the Nginx Configuration Still Parses

Timeout changes, upstream edits, and copied snippets should always pass a syntax test before reload:

sudo nginx -t

A successful syntax test prints:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

If Nginx reports a syntax error, fix that first. A configuration that does not parse can leave the old working config active, so later timeout edits may not actually be in use.

Check Whether the 504 Reaches the Origin

Send a request directly to the origin while preserving the host header. Replace 203.0.113.10 with the origin IP address:

curl -I --resolve example.com:80:203.0.113.10 http://example.com/

For HTTPS origins, test the TLS endpoint directly:

curl -I --resolve example.com:443:203.0.113.10 https://example.com/

If the direct origin request works but the public request still returns 504, inspect the CDN, load balancer, proxy manager, or firewall path between clients and the origin. If the direct origin request also returns 504, continue on the Nginx host.

Fix Nginx 504 for Reverse Proxy Backends

Reverse proxy 504 errors usually involve proxy_pass, an upstream block, or an application server behind Nginx. The Nginx reverse proxy configuration guide covers the base proxy pattern; this section focuses on timeout-specific checks.

Test the Nginx Upstream Directly

Find the upstream target in your Nginx configuration first:

sudo nginx -T 2>/dev/null | grep -E '^[[:space:]]*(proxy_pass|upstream|server[[:space:]]+[0-9a-zA-Z_.:-]+)'

Relevant lines might show a loopback backend or a named upstream group:

proxy_pass http://127.0.0.1:3000;
upstream app_backend {
    server 127.0.0.1:3000;
}

Test that backend from the Nginx server without going through the public virtual host:

curl -fsS http://127.0.0.1:3000/health

A healthy JSON endpoint might return:

{"status":"ok"}

If the direct backend request is slow, hangs, or fails, fix the application first. Increasing proxy_read_timeout only makes Nginx wait longer for a backend that is already unhealthy.

Check the Upstream Listener for Nginx

Confirm that the backend process is listening on the address and port from proxy_pass:

sudo ss -tulpn | grep ':3000'

Relevant output should show a process listening on the expected address:

tcp LISTEN 0 511 127.0.0.1:3000 0.0.0.0:*

If the backend listens only on a container network, private IP, or Unix socket, the proxy_pass target must match the address that the Nginx worker can actually reach. When path handling also looks wrong, compare your setup with the Nginx proxy_pass trailing slash explanation.

Set Nginx Proxy Timeouts for the Affected Location

Use timeout settings only after the upstream has passed a direct health check. Put them in the location that serves the slow route, not in every virtual host by default.

location /reports/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    proxy_connect_timeout 10s;
    proxy_send_timeout 60s;
    proxy_read_timeout 120s;
}

proxy_read_timeout is not a full request duration guarantee. It controls the timeout between successive reads from the upstream. If the upstream streams data periodically, the request can stay open longer than the numeric value. If the upstream sends nothing until the job finishes, Nginx can still time out before the first response header or body chunk arrives.

Test and reload after editing the site configuration:

sudo nginx -t && sudo systemctl reload nginx

The syntax test should pass before the reload applies the timeout change:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Check the Application Service Behind Nginx

When the backend is a systemd service, check whether the service is active:

systemctl is-active app.service

A healthy service state prints:

active

Then inspect recent logs for worker restarts, timeouts, database waits, or queue exhaustion:

sudo journalctl -u app.service --since "30 minutes ago"

Replace app.service with the real unit name. A backend that is active but saturated can still time out. Check worker counts, database queries, external API calls, and queue depth before making the Nginx timeout much larger.

Fix Nginx 504 for PHP-FPM and FastCGI

PHP-FPM 504 errors usually appear when PHP takes too long to generate a response. A wrong PHP-FPM socket more often produces a 502, but a slow PHP worker, long database query, blocked filesystem operation, or strict FPM execution limit can produce a 504.

Check PHP-FPM Service and Logs

Find the PHP-FPM service on your system:

systemctl list-unit-files --no-pager 'php*fpm*.service' 'php-fpm.service'

On some Debian and Ubuntu systems, the unit is versioned, such as php8.3-fpm. On Fedora, RHEL, Rocky Linux, and similar systems, the unit is often php-fpm. Use the unit name your package installed when checking its state:

systemctl is-active php-fpm

A healthy PHP-FPM service prints:

active

Replace php-fpm with the detected versioned unit when your distribution uses one.

Check recent PHP-FPM logs with the detected unit name:

sudo journalctl -u php-fpm --since "30 minutes ago"

Look for slow script warnings, worker exhaustion, terminated requests, upstream application errors, or database connection waits. If PHP-FPM is stopped or the socket path is wrong, use the Nginx 502 Bad Gateway troubleshooting guide instead because that is a different failure class.

Set Nginx FastCGI Timeout for Slow PHP Routes

For PHP routes that legitimately need more time, increase fastcgi_read_timeout in the PHP location. Keep the value scoped and realistic.

location ~ \.php$ {
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

    fastcgi_connect_timeout 10s;
    fastcgi_send_timeout 60s;
    fastcgi_read_timeout 120s;
}

Adjust the fastcgi_pass value to match your PHP-FPM socket or TCP listener. The socket path above is an example, not a universal path.

Validate and reload after editing:

sudo nginx -t && sudo systemctl reload nginx

Check PHP Execution Limits Behind Nginx

If Nginx waits longer but PHP-FPM still kills the request, check PHP and pool-level limits. The exact files differ by distribution and PHP version, so search the common locations:

sudo grep -R "^[[:space:]]*max_execution_time" /etc/php* /etc/php.d /etc/php.ini 2>/dev/null
sudo grep -R "^[[:space:]]*request_terminate_timeout" /etc/php* /etc/php-fpm.d 2>/dev/null

Relevant lines can show whether PHP or PHP-FPM stops the script before Nginx receives a response:

max_execution_time = 30
request_terminate_timeout = 0

Do not raise PHP execution limits for the whole server unless the application requires it. Long imports, exports, cache rebuilds, and report generation usually work better as queued jobs with a status page than as one long PHP request.

Fix Nginx 504 for Containers and Private Upstreams

Containerized applications add another networking layer. A 504 can happen when Nginx points to the wrong published port, an internal-only container address, a service name that only exists inside Docker, or a backend that responds slowly under container resource limits.

Check the Host Port Published to Nginx

List running containers and their published ports:

docker ps --format 'table {{.Names}}\t{{.Ports}}'

Relevant output should show the host port that Nginx can reach:

NAMES     PORTS
webapp    127.0.0.1:3000->3000/tcp

Test the published host port from the Nginx server:

curl -fsS http://127.0.0.1:3000/health

If Nginx runs on the host, proxy_pass http://webapp:3000; works only when the host can resolve webapp in the relevant network context. Many Docker Compose service names resolve only from containers on the same Compose network. Use the published host port, place Nginx in the same network, or use the correct upstream address for your architecture.

Check Container Logs for Slow or Stuck Requests

Inspect the backend container logs for application-level waits or crashes:

docker logs --since 30m webapp

Replace webapp with the container name. If the log shows database waits, worker restarts, memory pressure, or request queueing, fix the application or resource problem before treating Nginx as the root cause.

Fix Nginx 504 Behind a CDN or Load Balancer

A CDN, cloud load balancer, reverse proxy appliance, or hosting panel can generate its own 504 response. The origin Nginx server might be healthy while the outer layer cannot reach it, cannot complete TLS, hits its own idle timeout, or connects to a different origin address than expected.

Compare Edge and Origin Requests

Check the public path first:

curl -I https://example.com/slow-route

Then test the origin directly with the same hostname mapped to the origin IP address:

curl -I --resolve example.com:443:203.0.113.10 https://example.com/slow-route

If the origin request succeeds but the edge request times out, check the edge timeout, origin firewall, origin certificate, proxy protocol settings, health checks, and target pool. If both requests time out, inspect the origin Nginx and upstream logs.

Check Whether Nginx Saw the Timed-Out Request

Search the origin access log for the timed-out URI:

sudo grep ' /slow-route ' /var/log/nginx/access.log | tail -20

If there is no matching request, the request likely timed out before reaching this Nginx instance or reached a different virtual host/log file. Confirm DNS, load-balancer target settings, firewall rules, and per-site log paths before changing origin timeouts.

Tune Nginx Timeouts Safely

Timeouts should match the job and the upstream type. A public homepage, API request, WebSocket connection, background export, and PHP admin import do not need the same timeout profile.

Request TypeBetter Nginx ScopeNotes
Normal public pagesKeep defaults or modest valuesLong waits usually point to an application or database problem.
Admin reports or exportsSpecific admin locationUse a longer timeout only for the route that needs it.
Large uploads to an appUpload endpointCheck body-size limits and app upload handling as well as send/read timeouts.
Streaming responsesStreaming locationThe upstream should send periodic data or heartbeats if the connection must stay open.
PHP imports or migrationsPHP location plus PHP-FPM limitsNginx and PHP-FPM limits must not contradict each other.

For slow reverse-proxy routes, a scoped block like this is safer than raising timeouts for every site:

location /admin/export/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_connect_timeout 10s;
    proxy_send_timeout 60s;
    proxy_read_timeout 180s;
}

For slow PHP routes, use FastCGI timeouts instead of proxy timeouts:

location ~ \.php$ {
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_read_timeout 120s;
}

For uWSGI applications, use the matching uWSGI directive instead:

location /app/ {
    include uwsgi_params;
    uwsgi_pass 127.0.0.1:3031;
    uwsgi_read_timeout 120s;
}

Do not mix timeout families. proxy_read_timeout affects proxy_pass. fastcgi_read_timeout affects fastcgi_pass. uwsgi_read_timeout affects uwsgi_pass. If the wrong upstream family owns the request, the directive will not fix the timeout.

Verify the Nginx 504 Gateway Timeout Fix

After applying the focused fix, test the configuration, reload Nginx, and repeat the failing request.

sudo nginx -t && sudo systemctl reload nginx

The syntax test should still show a clean configuration:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Check the failing URL again:

curl -I https://example.com/slow-route

A successful response should no longer return 504:

HTTP/2 200

Then check the error log again to confirm the timeout message stopped during the test window:

sudo tail -n 50 /var/log/nginx/error.log

If the status improved but the route is still slow, keep the application performance issue on the work list. Nginx can wait longer, but it cannot make the upstream faster.

Troubleshoot Common Nginx 504 Gateway Timeout Problems

Use these symptom-focused checks when the first timeout change does not solve the problem. Replace example ports, routes, and service names with the values from your Nginx configuration, then retest with sudo nginx -t, a reload, and the failing request.

Nginx Error Log Shows Timeout While Reading Response Header

This means Nginx connected to the upstream, but the upstream did not send response headers in time. Match the log phrase, then test the backend route directly from the Nginx host:

sudo tail -n 200 /var/log/nginx/error.log | grep 'while reading response header'
curl -fsS http://127.0.0.1:3000/health

If the direct backend request is also slow, check application routes, database queries, external API calls, worker saturation, and upstream logs. If the route is expected to take longer and the backend is otherwise healthy, increase proxy_read_timeout or fastcgi_read_timeout for that route only.

Nginx Error Log Shows Timeout While Connecting to Upstream

This points to a listener, firewall, route, DNS, or network problem. Compare the address in proxy_pass with the actual listener and a direct request:

sudo ss -tulpn | grep ':3000'
curl -fsS http://127.0.0.1:3000/health

If the listener is missing or bound to a different address, fix the backend service, container port, firewall path, or upstream target before changing Nginx timeouts. A higher proxy_connect_timeout rarely fixes a backend that is unreachable.

Nginx 504 Appears Only After Uploads

Uploads can fail at several layers. Check whether the timeout happened while Nginx was sending the request body upstream, then inspect upload-related directives:

sudo tail -n 200 /var/log/nginx/error.log | grep 'while sending request to upstream'
sudo nginx -T 2>/dev/null | grep -E 'client_max_body_size|proxy_request_buffering|proxy_send_timeout|proxy_read_timeout'

If the error matches request-body sending, review upstream upload handling, temporary storage, application logs, and edge proxy limits. Increase proxy_send_timeout only when the backend can receive the upload but needs more time between write operations.

PHP-FPM Still Times Out After Nginx Changes

Nginx timeout settings do not override every PHP or PHP-FPM limit. Confirm the installed FPM unit, then search for PHP and pool-level runtime limits:

systemctl list-unit-files --no-pager 'php*fpm*.service' 'php-fpm.service'
sudo grep -R -E "^[[:space:]]*(max_execution_time|request_terminate_timeout)" /etc/php* /etc/php-fpm.d /etc/php.ini 2>/dev/null

If PHP-FPM kills the request first, align fastcgi_read_timeout, PHP execution time, pool limits, and application-level timeouts for the affected route. If the PHP-FPM socket is missing or inaccessible, the problem is closer to a 502 than a 504.

Nginx 504 Has No Matching Origin Log Entry

No matching origin log entry usually means the request did not reach this Nginx instance, reached another virtual host, or was logged somewhere else. Search the expected access log and confirm which logs the active virtual host writes to:

sudo grep ' /slow-route ' /var/log/nginx/access.log | tail -20
sudo nginx -T 2>/dev/null | grep -E '^[[:space:]]*(server_name|access_log|error_log)'

If the route is absent from origin logs, check the CDN or load balancer, DNS, firewall rules, default server selection, and per-site log configuration before changing origin timeouts. The Nginx server blocks and virtual hosts guide helps when the request may be landing in the wrong virtual host.

Nginx 504 Becomes Nginx 502 After Editing Upstreams

A 502 after an upstream edit often means the timeout problem turned into a connection, socket, protocol, or invalid-response problem. Search the error log for the stable 502 clues:

sudo tail -n 100 /var/log/nginx/error.log | grep -E 'connect\(\) failed|Permission denied|no live upstreams|upstream prematurely closed'

Recheck proxy_pass, fastcgi_pass, upstream names, container ports, and PHP-FPM socket paths. Use the Nginx 502 troubleshooting workflow when the log shows connection refused, missing sockets, permission denied, or invalid upstream headers.

Nginx 504 Returns Only for One Path

One-path timeouts usually come from a route-specific application problem or a different location block. Print the active locations and upstream handoffs, then compare the failing URI with the matching block:

sudo nginx -T 2>/dev/null | grep -E '^[[:space:]]*(location|proxy_pass|fastcgi_pass)'

Apply the timeout or upstream fix in the location that actually handles the URI. The Nginx location block priority guide helps when prefix, exact, and regex locations overlap.

Conclusion

A reliable Nginx 504 Gateway Timeout fix is scoped to the layer that owns the wait: origin reachability, upstream listener, application service, PHP-FPM pool, container mapping, CDN or load-balancer path, or a focused timeout. Keep the Nginx log guide, Nginx 502 Bad Gateway guide, and proxy_pass trailing slash guide nearby when upstream behavior points to a neighboring failure.

Follow LinuxCapable

Want more LinuxCapable guides in Google?

Add LinuxCapable as a preferred source so Google can show more of our fresh Linux tutorials in Top Stories and From your sources when relevant.

Add LinuxCapable as a preferred source on Google
Search LinuxCapable

Need another guide?

Search LinuxCapable for package installs, commands, troubleshooting, and follow-up guides related to what you just read.

Found this guide useful?

Support LinuxCapable to keep tutorials free and up to date.

Buy me a coffeeBuy me a coffee
Before commenting, please review our Comments Policy.
Formatting tips for your comment

You can use basic HTML to format your comment. Useful tags currently allowed in published comments:

You type Result
<code>command</code> command
<strong>bold</strong> bold
<em>italic</em> italic
<blockquote>quote</blockquote> quote block

Got a Question or Feedback?

We read and reply to every comment - let us know how we can help or improve this guide.

Let us know you are human: