A 504 Gateway Timeout looks like its cousin, the 502, but the cause is completely different. A 502 means your web server reached out to the backend and got a broken or refused connection. A 504 means it reached the backend just fine — and then waited, and waited, and gave up before anything came back. If you're chasing a 502 fix and it isn't working, there's a good chance you're actually looking at a 504 in disguise. Here's how to tell them apart and actually fix the timeout instead of just bumping numbers until the error goes away for a week.

Symptom: What a 504 Looks Like

The exact page varies depending on what's in front of your app, but the pattern is consistent:

  • A plain "504 Gateway Time-out" page with "nginx" printed at the bottom
  • Cloudflare's error 524 ("A timeout occurred") — Cloudflare's own flavor of the same problem
  • It happens on specific actions: a big CSV import, a WooCommerce checkout with a slow payment gateway, a report page that runs a heavy SQL query, a plugin install pulling from a slow source
  • Quick pages load fine. It's always the same one or two slow screens that fail
  • The delay before the error is suspiciously consistent — exactly 30, 60, or 90 seconds, which is your first clue about which timeout setting is firing

That last point matters. Whoever configured your stack picked timeout values somewhere, and the number on the error page (or the time it takes to appear) tells you which layer gave up first.

502 vs 504 vs 500: Don't Mix These Up

ErrorWhat it actually means
500 Internal Server ErrorThe backend responded, but with an error — a PHP fatal error, an uncaught exception, a broken .htaccess rule
502 Bad GatewayThe proxy couldn't get a valid response at all — backend is down, crashed, or refused the connection
504 Gateway TimeoutThe proxy got no response in time — the backend is alive and working, just too slow for this request

People often try to fix a 504 by restarting PHP-FPM or Nginx, the way you'd fix a 502. That doesn't help here, because nothing is actually down — it's just slow. Restarting the service just clears any stuck workers, which can mask the symptom for a bit without touching the real cause.

Cause: Why the Backend Didn't Answer in Time

CauseWhat's happening
Slow SQL queryA report, search, or import triggers a query with no index, scanning a huge table and taking minutes instead of milliseconds
External API callYour PHP script is waiting on a payment gateway, shipping API, or third-party webhook that's slow or unresponsive
PHP-FPM pool exhaustedAll worker processes are tied up with other slow requests, so yours sits in the queue until the proxy's patience runs out
Timeout mismatch between layersNginx gives up at 60s but PHP-FPM's own request_terminate_timeout is set to 300s — the frontend never waits long enough for the backend to actually finish
Cloudflare's hard 100-second capCloudflare kills any proxied connection at 100 seconds no matter what your origin server is configured for — this shows as error 524
Large, synchronous bulk operationsA CSV import or database migration that processes everything in one HTTP request instead of a background job

Fix: Find the Slow Layer, Then Fix It There

1. Check the Nginx error log first

It usually names the exact directive that fired:

tail -100 /var/log/nginx/error.log

Look for a line like upstream timed out (110: Connection timed out) while reading response header from upstream. That confirms Nginx gave up waiting on PHP-FPM (or whatever's behind it), and tells you which config to adjust.

2. Raise Nginx's proxy timeouts — but only for the routes that need it

Inside your server block or a specific location, not globally for the whole site:

location /wp-admin/admin-ajax.php {
    proxy_read_timeout 180s;
    proxy_connect_timeout 180s;
    fastcgi_read_timeout 180s;
}

If you're proxying straight to PHP-FPM over FastCGI rather than a separate app, fastcgi_read_timeout is the one that actually matters — proxy_read_timeout only applies to proxy_pass upstreams. Bumping global timeouts site-wide is a bad habit: a stuck request will now tie up a worker for three minutes instead of one, and under load that makes things worse, not better.

3. Match PHP-FPM's own timeout

Check request_terminate_timeout in your pool config, usually at /etc/php/8.1/fpm/pool.d/www.conf:

request_terminate_timeout = 180s

This needs to be equal to or higher than whatever Nginx is waiting for. If PHP-FPM's limit is shorter, the worker gets killed mid-script before Nginx even times out, and you'll see a 502 instead — which is why these two errors get confused so often. Reload FPM after changing it:

systemctl reload php8.1-fpm

4. On Apache, it's the Timeout and ProxyTimeout directives

If you're running mod_proxy in front of an app, or a straightforward Apache setup on shared hosting:

Timeout 180
ProxyTimeout 180

These go in the vhost config or httpd.conf; on shared cPanel hosting you generally can't touch these yourself, so this is a support-ticket fix rather than a File Manager one.

5. Cloudflare's 524 needs a different move entirely

If you're seeing error 524, raising your origin server's timeouts won't help past 100 seconds — Cloudflare's proxy layer cuts the connection regardless. Your real options are: make the request faster (see below), move the operation to a background job that returns immediately, or, for legitimate long-running admin tasks, bypass Cloudflare's proxy for that specific path (grey-cloud a subdomain in DNS) so it connects directly to your origin.

6. Fix the actual slow operation

Raising timeouts buys you time, it doesn't fix why the request is slow. Worth checking:

  • Enable MySQL's slow query log and look for anything without an index: SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 2;
  • For WordPress imports, use WP-CLI or a plugin that chunks the import into batches instead of one giant request
  • Move anything that calls an external API — payment confirmation, shipping rate lookups — onto a queue (Action Scheduler in WooCommerce, or a proper job queue on a custom app) so the user isn't waiting on a live HTTP request for it

Prevention

  • Keep timeout values consistent across every layer — Cloudflare, Nginx, PHP-FPM — increasing outward, not just at the one you can see
  • Never raise a global timeout to fix one slow page; scope the change to the specific location or endpoint
  • Turn on the MySQL slow query log on any site handling real traffic, and actually check it once in a while
  • Move bulk work — imports, exports, migrations, report generation — to cron jobs or background queues instead of a single HTTP request
  • If you're on a SkyServer VPS and regularly hitting timeouts on legitimate heavy tasks, that's often a sign the plan's CPU or PHP-FPM worker count needs a bump rather than another round of config tweaks

Frequently Asked Questions

What's the difference between a 502 and a 504 error?

A 502 means the proxy couldn't get any usable response from the backend — it's down, crashed, or refused the connection. A 504 means the backend was reachable but didn't respond within the allowed time. Same messenger, different problem: one is "nobody answered," the other is "someone's on the line but taking too long."

Will just increasing the timeout value fix this permanently?

Sometimes, if the operation is naturally slow but finite — a big but one-time import, for example. But if the same page keeps hitting the new, higher timeout too, you have a query or process that's actually broken or unbounded, and no timeout value will be high enough forever.

Why do I get a 504 on Cloudflare even after raising my origin server's timeout?

Cloudflare enforces its own hard limit around 100 seconds on proxied (orange-cloud) connections, independent of your server config. Origin-side changes help up to that ceiling; past it, you need to speed up the request itself or move it off Cloudflare's proxy.

My site is on shared cPanel hosting — can I change these timeout settings myself?

Usually not directly. Apache and PHP-FPM pool settings on shared hosting are managed at the server level, not per-account. Open a support ticket describing the specific page and how long it runs before failing — that's enough for a host to raise the right limit or suggest moving the task to a cron job instead.

Is a 504 error a sign my server needs more resources?

Not always. A single slow, unindexed query will time out on a powerful server just as easily as a small one. Check the actual cause first — logs and the slow query log — before assuming the fix is a bigger VPS plan.