504 Gateway Timeout means the go-between (NginxNginx A fast, popular program that sends web pages to visitors and can pass requests on to apps behind it. More about Nginx →, Apache, a load balancer or a CDN) waited for the program behind it and gave up before it answered. Unlike a 502, the backend is usually running. It is too slow, stuck waiting on something, or overloaded.
If you are visiting the site
Wait a minute and reload. If the page does something heavy, such as an export, a report or a large search, it may keep timing out until the site owner fixes it. Other pages on the same site often still work.
Which timeout ran out?
Every layer between the visitor and your code has its own clock. The 504 comes from the first one to give up.
| Layer | Default limit | Setting |
|---|---|---|
| Cloudflare (free and Pro plans) | 100 seconds | Fixed; Enterprise plans can raise it |
| AWS Application Load Balancer | 60 seconds | Idle timeout on the load balancer |
| Nginx to PHP-FPM | 60 seconds | fastcgi_read_timeout |
| Nginx to an app | 60 seconds | proxy_read_timeout |
| Apache to PHP-FPM | 60 seconds on most setups | ProxyTimeout or Timeout |
| PHP-FPM | off unless set | request_terminate_timeout |
| PHP | 30 seconds | max_execution_time |
A request that fails at almost exactly 60 seconds is hitting a 60-second limit. One that fails at 100 seconds behind Cloudflare is Cloudflare’s limit. Time it:
curl -o /dev/null -s -w "%{http_code} after %{time_total}s\n" https://example.com/slow-page/
If you run the site
1. Find which requests time out
sudo grep "upstream timed out" /var/log/nginx/error.log | tail -n 20
A typical line:
upstream timed out (110: Connection timed out) while reading response header from upstream, client: 203.0.113.7, server: example.com, request: "GET /report/ HTTP/2.0", upstream: "fastcgi://unix:/run/php/php8.3-fpm.sock"
The request part names the page. Is it every page, or one? One slow page points to slow code or a slow query. Every page points to an overloaded server or a shared dependency, such as the database, being down or swamped.
2. Check whether the server is overloaded
uptime
top -o %CPU
free -h
uptime shows load averages for the last 1, 5 and 15 minutes. Compare them with your CPU count (nproc). A load well above that number means requests are queueing for CPU. If memory is nearly full and swap is busy, everything slows down at once.
If the server is simply too small for its traffic, the lasting fixes are page caching, a CDN for static files, or more resources. See how much RAM a website needs.
3. Look for slow database queries
On MySQL or MariaDB, list what is running right now:
sudo mysql -e "SHOW FULL PROCESSLIST;"
Look at the Time column. Queries running for many seconds, or many queries stuck in Locked or Waiting for table metadata lock, are the usual culprit. To catch slow queries over time, turn on the slow query log:
sudo mysql -e "SET GLOBAL slow_query_log = 1; SET GLOBAL long_query_time = 2;"
sudo tail -f /var/lib/mysql/$(hostname)-slow.log
Common causes are a missing index on a large table, a plugin running a report on every page load, or a WordPress wp_options table bloated with autoloaded data.
4. Find where the PHP time goes
PHP-FPMPHP-FPM The part of the server that runs PHP code, the language WordPress and many sites are written in, and hands the finished page to the web server. More about PHP-FPM → can log the stack trace of any request that runs too long. Add this to the pool file and restart PHP-FPM:
request_slowlog_timeout = 10s
slowlog = /var/log/php-fpm/slow.log
Each entry shows the function the script was stuck in, which usually names the plugin, theme or library at fault.
5. Check calls to other services
Pages that call another service, such as a payment API, a licence server, a feed or a remote font, wait for it. If that service is slow or unreachable, your page waits too, and a firewallFirewall A gatekeeper that decides which connections are allowed into a server and blocks the rest. More about Firewall → that silently drops outgoing traffic makes it wait until the timeout. Test from the server:
curl -o /dev/null -s -w "%{time_total}s\n" https://api.example-service.com/
In your code, give every outgoing call a short timeout of its own, so one slow service cannot hold a page for a minute.
6. Raise timeouts only for genuinely long work
If a task really needs longer, such as a large import or a report, raise the limits together, for that location only where you can:
# Nginx, in the location block for the slow URL
fastcgi_read_timeout 300;
# PHP-FPM pool
request_terminate_timeout = 300
# php.ini or the pool (php_admin_value[max_execution_time])
max_execution_time = 300
Test and reload Nginx, then restart PHP-FPM:
sudo nginx -t && sudo systemctl reload nginx
sudo systemctl restart php8.3-fpm
For apps behind proxy_pass, the Nginx setting is proxy_read_timeout. Behind Cloudflare, anything longer than 100 seconds still fails, so move long jobs out of the web request: run them in the background with a queue or a cron jobCron job A task the server runs by itself on a timetable, like an alarm clock for jobs. For example, making a backup every night at 3 a.m. More about Cron job →, and show the user a “we’ll email you when it’s ready” message instead.
Do not raise timeouts across the whole site to hide a slow page. Visitors still wait, and each stuck request holds a PHP worker that other visitors need.
On shared hosting
You usually cannot change server timeouts. A 504 there means the page is too slow for the host’s limits, or the account is hitting its CPU cap. Check the control panelControl panel A website you log into to manage your hosting with buttons and forms, instead of typing commands. cPanel and Plesk are the common ones. More about Control panel →’s resource usage page, disable heavy plugins (backup, statistics and broken-link checkers are common), and add a caching plugin.
Confirm it is fixed
Time the page again with curl as above. A healthy dynamic page answers in well under a second; anything over a few seconds will time out again under load. Then watch the log while you browse:
sudo tail -f /var/log/nginx/error.log | grep --line-buffered "timed out"
502, 503 or 504?
| Code | What happened |
|---|---|
| 502 Bad Gateway | The backend gave no answer or a broken one |
| 503 Service Unavailable | The server is up but refusing work: maintenance or overload |
| 504 Gateway Timeout | The backend was too slow to answer |
Related
- What caching is, the most effective fix for a server that cannot keep up.
- What a reverse proxy is.
Something out of date? Software changes. If a step no longer works, tell us and we will check it and update the page.



