Logo
WP Fix by Blimx

WordPress 504 Gateway Timeout — The Definitive Playbook

Actualizado:
ErrorsPerformance

What a 504 is really telling you

A 504 says the proxy waited for WordPress to respond, the response didn't come within the time budget, the proxy gave up. The difference with 502 matters: 502 means broken response, 504 means no response in time.

The interesting part is that 504s usually have a clear, identifiable culprit somewhere — a single slow query, a third-party API stall, a runaway loop. This playbook is how we find that culprit every time.

The timeout chain

Each layer has its own timeout. The 504 originates at the layer with the shortest timeout that the underlying request exceeded:

Cloudflare: 100s (free), 6000s (Enterprise)
Nginx fastcgi_read_timeout: 60s (default)
PHP max_execution_time: 30s (default)
MySQL max_execution_time: 0 (no limit, default)

Knowing which layer fired first narrows the diagnosis instantly.

Cause 1 — A specific PHP request takes >30 seconds

Most common 504. A page on your WordPress site genuinely takes longer than PHP's max_execution_time to render.

Identify the slow request

Enable PHP-FPM slow log:

; /etc/php/8.1/fpm/pool.d/www.conf
slowlog = /var/log/php-fpm/www.slow.log
request_slowlog_timeout = 10s

Reload PHP-FPM. Reproduce the 504. Examine the slow log — it will name the exact PHP file and line that was running when the timeout hit.

Fix

Once you know the slow code: - If it's a plugin doing heavy work on page load → background it (Action Scheduler, cron) - If it's a slow database query → optimize the query (add index, EXPLAIN, refactor) - If it's a third-party API call → add wp_remote_request timeout: 5s and graceful fallback

For unavoidable slow operations (large exports), increase limits surgically rather than globally:

// At the top of the slow script only
set_time_limit(300);
ini_set('memory_limit', '1G');

Cause 2 — Slow MySQL query

The query takes >30s. PHP waits, then times out.

Identify

Enable MySQL slow query log:

SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 5;
SET GLOBAL slow_query_log_file = '/var/log/mysql/mysql-slow.log';

Reproduce the 504. Examine the slow log.

Fix

  • Add indexes — most WordPress slow queries are wp_postmeta scans missing an index on (post_id, meta_key)
  • Refactor query — meta_query arrays often produce inefficient SQL; use tax_query or custom tables for heavy queries
  • Switch to caching — cache the result with wp_cache_set or transients

Cause 3 — External HTTP call inside PHP request

A plugin (social share counter, OEMBED, Mailchimp sync) makes an outbound HTTP call. The remote API is slow or unreachable. Your PHP request blocks until the call times out.

Identify

In Query Monitor → HTTP API Calls, look for outbound calls during page load. Times in seconds = your bottleneck.

Fix

Set sane timeouts for all wp_remote_* calls. Most plugins use defaults of 30+ seconds.

$response = wp_remote_get($url, array(
    'timeout' => 5,
));

For plugins you don't control, you can override via filter:

add_filter('http_request_timeout', function($t) { return 5; });

Cause 4 — Cloudflare's 100-second hard timeout

Even with everything else optimized, Cloudflare's free/pro tier has a hard 100-second timeout on origin requests. Long-running admin operations hit it.

Identify

You see the Cloudflare-branded 504 page (their colors and logo). Check Cloudflare → Audit Logs for "origin connection timeout."

Fix

  • Move long operations to background jobs (always the right answer)
  • For admin-only operations, bypass Cloudflare on /wp-admin/ paths (Page Rules → Cache Level: Bypass, Disable Performance)
  • For Enterprise customers, Cloudflare allows raising the timeout

You cannot raise the timeout on Free or Pro plans.

Cause 5 — Blocking lock in the database

A long-running write query holds a lock. Subsequent queries wait, exceed timeout. The waiting queries log 504.

Identify

SHOW PROCESSLIST;
SHOW ENGINE INNODB STATUS\G

Look for queries in Locked state or rows with WAITING FOR LOCK.

Fix

  • Kill the long query: KILL <connection_id>
  • Investigate why a query is taking so long (usually an UPDATE on a non-indexed column)
  • Consider switching to SET TRANSACTION ISOLATION LEVEL READ COMMITTED for read queries that don't need locks

Cause 6 — DNS resolution timing out

An outbound HTTP call from WordPress depends on DNS. The server's DNS resolver is slow or unreachable. Resolution alone takes 30s.

Identify

dig +short api.someservice.com
time curl -v https://api.someservice.com/ 2>&1 | grep "Connected\|DNS"

If dig is slow or hangs, this is the cause.

Fix

Configure a fast DNS resolver on the server:

# /etc/resolv.conf
nameserver 1.1.1.1
nameserver 8.8.8.8
options timeout:1 attempts:2

Or install a local caching resolver (dnsmasq, unbound).

The 504 diagnostic flow

When the next 504 happens:

# Step 1 — when did the 504 happen?
date -d "@$(stat -c %Y /var/log/nginx/error.log)"

# Step 2 — what was running at that time?
tail -200 /var/log/php-fpm/www.slow.log

# Step 3 — what was MySQL doing?
grep -A 10 "Query_time" /var/log/mysql/mysql-slow.log | tail -50

# Step 4 — was the call going outbound?
tcpdump -i any port 443 -nn -c 50 2>/dev/null   # while reproducing

This sequence finds the slow component in under 10 minutes.

Common mistakes when diagnosing 504

  • Raising every timeout to 600s — hides the slow code, doesn't fix it
  • Restarting MySQL when a query is stuck — terminates the lock holder but the cause (bad query) returns next time
  • Disabling Cloudflare — makes the site unprotected; the 504 was a symptom not the cause
  • "Just upgrade hosting" — most slow-WordPress 504s have a plugin or query cause, not a server cause

When to call a specialist

If you've enabled slow logs and the offending code is in a plugin you can't easily replace, we do this rewrite/optimization work routinely. Most 504-prone WordPress sites can be fixed in 2-4 hours of profiling + optimization.

504 emergency support within minutes. For broader performance issues see WordPress speed recovery.