504 Gateway Timeout: what it means and how to fix it
· 6 min read
What the error means
A 504 is the web server in front (nginx, Apache, a load balancer, a CDN) giving up on the application behind it. The request was passed on, the backend started working, and the front's timer ran out before an answer came back. The difference to a 502 Bad Gateway is that the backend is reachable; it is just too slow. Typical timers are 60 seconds in nginx (fastcgi_read_timeout, proxy_read_timeout), 60 seconds in Apache (ProxyTimeout) and 100 seconds at Cloudflare, which you cannot change on the free plan.
Raising the timeout is rarely the fix. Something takes too long, and that something is what you need to find.
Common causes
- A slow database query. A WordPress plugin that counts rows in a huge table on every page load, a missing index, a
wp_optionstable full of expired transients. Every request waits, and under load the waits add up. - An external call that hangs. The application contacts a payment provider, a mail service, a weather API or a license server, and that service is slow or down. Without a timeout on that call, your page waits for it.
- Too few workers. All PHP-FPM workers are busy with slow requests; new ones queue until the front gives up. On the outside this looks like a timeout even though each single request would be fast.
- Cron or backup running in the foreground. A backup plugin, an import, a sitemap of 50,000 URLs: generated inside a web request, they exceed any timeout.
- Disk I/O or swap. The server runs out of memory and swaps; everything slows down tenfold. Visible as high load with low CPU use.
- Network between front and back. A load balancer in one data center, the backend in another, a firewall dropping packets silently. Rare, but a 504 on every request with an idle backend points here.
If you are a visitor
Reload after a minute. If the site is slow rather than down, try a different page; the timeout may be tied to one heavy page.
If it is your site: check first
Run the website down checker: it reports the status code and the response time from outside. A site that answers in 30 seconds today is a site that will show 504 under load tomorrow.
On your own server, find where the time goes:
# are workers piling up?
sudo tail -n 30 /var/log/nginx/error.log | grep -E 'timed out|timeout'
# what is PHP doing right now? (needs pm.status_path in the pool)
curl -s 'http://127.0.0.1/fpm-status?full' | grep -E 'state|request URI|duration' | head -40
# MySQL/MariaDB: which queries are running, and for how long?
sudo mysql -e 'SHOW FULL PROCESSLIST;'
# memory and swap
free -m
vmstat 1 5
A process list full of the same query, or si/so columns in vmstat that are not zero, names the cause.
Fixes on your own server
1. Turn on the slow query log and fix the query
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 2;
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
After a few minutes, sudo mysqldumpslow -s t /var/log/mysql/slow.log | head lists the worst queries. Add the missing index, or find the plugin that runs the query and replace it. In WordPress, a bloated options table is a classic:
wp transient delete --all
wp db query "SELECT option_name, LENGTH(option_value) AS size FROM wp_options WHERE autoload='yes' ORDER BY size DESC LIMIT 20;"
Anything in the hundreds of kilobytes that autoloads on every request is a candidate for removal.
2. Put a timeout on external calls
Every HTTP call your application makes needs its own timeout, shorter than the web server's. In PHP with cURL: CURLOPT_CONNECTTIMEOUT 5 and CURLOPT_TIMEOUT 10. In WordPress, wp_remote_get() takes a timeout argument; many plugins leave it at the default and wait forever. If a third-party service is down, the page should degrade (show the form without the live shipping rate), not die.
3. Move long work out of the request
Backups, imports, exports, big sitemaps and mail sending belong in a cron job or a queue, not in a page view. For WordPress, disable the pseudo-cron and run the real one:
// wp-config.php
define('DISABLE_WP_CRON', true);
# crontab -e
*/5 * * * * cd /var/www/example.com && wp cron event run --due-now >/dev/null 2>&1
4. Adjust the timeouts, with care
If a single page legitimately needs longer (an admin export, a report), raise the timeout for that location only, not globally:
location = /wp-admin/export.php {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_read_timeout 300;
}
Apache with PHP-FPM: ProxyTimeout 300 inside a <Location> block. PHP itself has max_execution_time; set it to the same value, or PHP stops first and you get a 500 instead. Then sudo nginx -t && sudo systemctl reload nginx.
5. Stop swapping
ps aux --sort=-%mem | head
If PHP-FPM workers each use 150 MB and you run 30 of them on a 4 GB server, the math does not work. Lower pm.max_children, raise memory, or fix the plugin that uses the memory. A server that does not swap is a server whose 504s mostly disappear.
6. Cache
A full-page cache (nginx FastCGI cache, a WordPress caching plugin, Varnish) serves repeat visitors without touching PHP or the database. Most 504s under load vanish once the home page and the top pages come from the cache.
When it is your host's problem
On shared hosting, you can fix slow queries and plugins but not the server's timeouts or the neighbors' load. Send your host:
Hello, my website example.com returns "504 Gateway Timeout" since about 16:00 UTC. Pages that used to load in under two seconds now time out. I have not changed anything. Could you check whether the database server or the PHP pool of my account is overloaded or whether another account on the server is causing high load, and tell me what you find?
Preventing the next one
A 504 is a slow site that got slower, and response time is measurable long before the timeout. Website checks record the response time of every check; set an alert on it, and you will see the trend weeks before visitors see an error. The server agent adds load, memory and swap from inside. On your own server, a deep fix finds the slow query or the hanging call and fixes it, billed in 15-minute blocks, price before you order.
What now
Check it from outside
Is the site down for everyone or just for you, and what does the server answer? Free, no sign-up.
Website down checkerDown? We step in.
On your own server or in a webspace with SSH access: a quick fix at a fixed price for the common causes, a deep fix billed in 15-minute blocks for the rest, around the clock. Price before you order.
Get it fixedMore guides
- Your website is down. Here is what to do in the first 15 minutes.
- Server down: what to check first, and in which order
- 502 Bad Gateway: causes and how to fix it
- 503 Service Unavailable: overloaded or in maintenance?
- DNS_PROBE_FINISHED_NXDOMAIN: causes and fixes
- ERR_CONNECTION_REFUSED: causes and how to fix it
- "Your connection is not private": the SSL certificate warning