WordPress troubleshooting

504 Gateway Timeout

PHP was still working when the web server gave up waiting. Nothing crashed - it simply took longer than the timeout allows.

504 Gateway Timeout

Short answer

Something in the request is slow, and raising the timeout only means waiting longer for the same page. Identify the slow step first: a query, an outbound API call, or an import running inside a web request.

Diagnosis

What causes it, most likely first.

Work down the list. Each check is written to rule its row in or out before you change anything, because the fixes below it are different and a guess costs more than a command.

CauseHow to checkFix
An outbound HTTP request with no timeoutThe slow URL calls a third party - a licence check, a feed, a payment gateway. It is fast when that service is fast.Set an explicit timeout on the outbound call and cache the response. WordPress waits far longer by default than any visitor will.
A query with no index behind itThe slow page is a filtered archive or a report, and the database shows the same query at the top under load.Add the index the query needs. A scan that was fine at ten thousand rows is a timeout at a million.
An import or export running in a web requestThe timeout only happens on an admin action you started, and it fails at roughly the same elapsed time each attempt.Move the job to WP-CLI or a chunked background run. A browser tab is the wrong place for work measured in minutes.
Cron piling up on page loadsThe delay lands on ordinary front-end URLs, sporadically, on a site with a long overdue-events list.Disable WP-Cron and drive it from the system scheduler, so scheduled work stops being charged to whichever visitor arrives first.
The timeout is genuinely too short for the workEverything above is ruled out and the operation is legitimately long - a large backup, a big media conversion.Raise the timeout for that path only. Raising it globally removes the ceiling that stops one bad request occupying a worker.
Walkthrough

Fixing it, step by step.

Commands assume WP-CLI and shell access. Where you have neither, each step says what it is doing so it can be done from the dashboard or over SFTP instead.

  1. Find out which URL is slow

    The access log records how long each request took. Sort by that and the answer is usually one line.

    awk '{print $NF, $7}' /var/log/nginx/access.log | sort -rn | head -n 10
  2. Ask the database what it is spending time on

    A long-running query shows up here while the page is still loading.

    mysql -e 'SHOW FULL PROCESSLIST;' | head -n 20
  3. Check whether cron is the passenger

    A long overdue list means scheduled work is being run by visitors.

    wp cron event list --status=due --fields=hook,next_run_relative
  4. Move the long job out of the request

    The same import that times out in a browser usually completes from the command line, because nothing is waiting on it.

    wp import large-file.xml --authors=create
  5. Only now, consider the timeout

    And raise it for the path that needs it rather than everywhere.

    wp config set WP_MAX_MEMORY_LIMIT '512M'
On MagicWP

What the platform takes off the list.

Scheduled work runs on a real system scheduler rather than on whichever visitor happens to arrive, so the most common accidental cause of a slow page is absent from the start. Long jobs have their own limits in the cron context rather than borrowing the ones meant for web requests, and per-site containers mean a request that does run long occupies your workers only.

FAQ

Questions, answered.

Should I just increase the timeout?
Rarely, and never first. A higher timeout means the visitor waits longer before seeing the same failure, and it removes the ceiling that stops a single request tying up a worker indefinitely.
Why does the same page load fine sometimes?
Because the slow ingredient is not always slow. A third-party API having a bad minute, or a query that is fine until the cache is cold, both produce a page that works until it does not.
My import times out at exactly the same point every time.
That is a limit rather than a coincidence. Run it from WP-CLI, or in chunks that record their position, so an interrupted run resumes rather than repeating.
Is a 504 caused by my visitors' connection?
No. It is generated between the web server and PHP, both on the server side. A visitor with a slow connection sees a slow page, not a 504.
Related

If that was not it.

These fail in ways that look similar from the browser.