How to Fix 504 Gateway Timeout on WordPress (Nginx & Apache)
504 Gateway Timeout on WordPress is a server-side error. Here is how to diagnose it on Nginx and Apache, fix PHP-FPM timeouts, and stop it happening again.
A 504 Gateway Timeout on WordPress means the web server (Nginx or Apache) gave up waiting for PHP-FPM to respond — fixing it requires increasing the fastcgi_read_timeout in Nginx or the ProxyTimeout in Apache, and identifying the slow query or plugin causing the long execution time.
A 504 Gateway Timeout means your web server (Nginx or Apache) sent a request to an upstream service - usually PHP-FPM or a proxy - and that upstream service took too long to respond. The server gave up waiting and returned an error to the visitor.
Unlike a 500 error (which usually means PHP crashed), a 504 means PHP is still running - it is just taking longer than the configured timeout. This distinction matters for diagnosing it.
Common scenarios where 504s appear on WordPress sites:
- A page that runs a slow database query (large WooCommerce stores, poorly optimised posts queries)
- A plugin making an external HTTP request that hangs (payment gateways, social media feeds, weather widgets)
- Too many concurrent requests overwhelming PHP-FPM's worker pool
- Running WP-CLI commands or bulk operations that legitimately take a long time
- A misconfigured proxy chain (Nginx > PHP-FPM, or Nginx > Apache > PHP-FPM)
Step 1 - Identify where the timeout is configured
A 504 can come from several layers. Check each timeout in order:
Nginx read timeout (most common)
If you run Nginx as a reverse proxy in front of PHP-FPM, the relevant directive is fastcgi_read_timeout:
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_read_timeout 120; # seconds
include fastcgi_params;
}
The default is 60 seconds. Increase it to 120 or 180 for sites with heavy processing.
After editing nginx config, always test before reloading:
sudo nginx -t sudo systemctl reload nginx
PHP-FPM timeout
PHP-FPM has its own request timeout. Edit your PHP-FPM pool config (usually /etc/php/8.1/fpm/pool.d/www.conf):
request_terminate_timeout = 120
This is the maximum time a PHP request is allowed to run. If your operation legitimately takes longer (bulk imports, PDF generation), increase it. But do not set it to 0 (unlimited) on production - that allows runaway scripts to hold FPM workers indefinitely.
sudo systemctl restart php8.1-fpm
PHP max_execution_time
Set in php.ini or per-site:
max_execution_time = 120
Or in .htaccess (Apache):
php_value max_execution_time 120
Or in wp-config.php:
set_time_limit( 120 );
Note: set_time_limit() resets the timer each time it is called and excludes time spent in external calls - so a script can run longer than the value if it is doing network I/O.
Apache ProxyTimeout (if using Apache as a reverse proxy)
ProxyTimeout 120
Load balancer / CDN timeout
If you have Cloudflare, AWS ALB, or another load balancer in front of your server, it also has its own timeout. Cloudflare's default proxy timeout is 100 seconds - requests that take longer than that will 524 (their custom code for origin timeout). Increase this in Cloudflare's Route Settings, or better, make the origin faster.
Step 2 - Find the slow request
Check your Nginx access log to identify which URL is timing out:
sudo grep "504" /var/log/nginx/access.log | tail -20
This shows you the URL, response time, and timestamp. A pattern like /wp-admin/admin-ajax.php timing out usually means a plugin's AJAX handler is running a slow query.
Enable Nginx response time logging to pinpoint slow requests:
log_format timed '$remote_addr [$time_local] "$request" $status '
'$body_bytes_sent $request_time';
access_log /var/log/nginx/access.log timed;
Any $request_time above your timeout threshold is a suspect.
Step 3 - Profile the slow database query
Most legitimate 504s on WordPress are caused by a slow SQL query. Enable MySQL slow query logging:
SET GLOBAL slow_query_log = 1; SET GLOBAL long_query_time = 2; -- log queries taking > 2 seconds SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
Or persistently in /etc/mysql/mysql.conf.d/mysqld.cnf:
slow_query_log = 1 slow_query_log_file = /var/log/mysql/slow.log long_query_time = 2 log_queries_not_using_indexes = 1
After a few minutes of traffic, read the log:
sudo tail -100 /var/log/mysql/slow.log
You will see queries like:
Query_time: 8.432 Lock_time: 0.000 Rows_sent: 1500 Rows_examined: 2500000 SELECT * FROM wp_postmeta WHERE meta_key = '_sku';
A Rows_examined value much higher than Rows_sent means the query is doing a full table scan. Add an index or rewrite the query.
For WordPress specifically, the most common slow queries are:
wp_postmetascans (WooCommerce product meta without indexes)wp_optionswithautoload = 'yes'rows accumulating- Custom meta queries using
LIKE '%value%'which cannot use indexes
Use EXPLAIN on the slow query to understand what is happening:
EXPLAIN SELECT * FROM wp_postmeta WHERE meta_key = '_sku';
Step 4 - Identify external HTTP requests causing timeouts
WordPress plugins frequently make outgoing HTTP requests - to payment gateways, weather APIs, social feeds, license servers. If any of these are slow or down, the PHP process hangs waiting, and you get a 504.
To diagnose, add this to wp-config.php temporarily:
define( 'WP_DEBUG', true ); define( 'WP_DEBUG_LOG', true );
Then check the debug log for HTTP timeouts. You can also use the Query Monitor plugin, which shows all external HTTP requests and their duration on any admin page.
A quick way to test if a specific external URL is slow from your server:
curl -o /dev/null -s -w "%{time_total}\n" https://api.example.com/endpoint
If the external service is reliably slow, the fix is to:
- Add a shorter timeout to the WordPress HTTP API call:
add_filter( 'http_request_timeout', fn() => 5 ); - Cache the API response with a transient so it is only fetched once every hour
- Replace the plugin with one that does not make blocking external requests on page load
Step 5 - PHP-FPM worker exhaustion
If your site gets sudden traffic spikes, PHP-FPM may run out of available workers. New requests queue up, then time out at the Nginx level. Check FPM status:
# Enable status page in pool config first pm.status_path = /status
Then:
curl http://127.0.0.1/status
You will see active processes, idle processes, and the queue length. If listen queue is consistently above 0, you are worker-constrained.
Increase the max children in your FPM pool config:
pm = dynamic pm.max_children = 50 ; max concurrent PHP processes pm.start_servers = 10 pm.min_spare_servers = 5 pm.max_spare_servers = 20 pm.max_requests = 500 ; recycle workers to prevent memory leaks
The right value for pm.max_children depends on your server RAM. Each PHP-FPM worker uses roughly 30-60 MB. On a 2 GB RAM VPS with MySQL also running, 20-25 children is a reasonable maximum.
Step 6 - Check for runaway cron jobs
WP Cron tasks that run a heavy operation (sending 1000 emails, regenerating all thumbnails) can monopolise PHP-FPM workers for minutes. When too many cron tasks stack up, they starve normal page requests of workers.
Check what is in the cron queue:
wp cron event list --format=table
If there are hundreds of pending events, something is broken. Clear stuck events:
wp cron event delete --all
Then identify and fix the plugin that is generating excessive cron jobs.
Preventing 504s long-term
Use a page cache
A full-page cache (WP Rocket, FlyingPress, LiteSpeed Cache, or server-side Nginx FastCGI cache) serves cached HTML without invoking PHP at all. Cached pages cannot 504. This is the single most effective protection against timeout errors under traffic spikes.
Move slow operations to background jobs
Any operation that might take more than 2 seconds should not run synchronously on page load. Use Action Scheduler (bundled with WooCommerce) or a plugin like WP Background Processing to queue slow tasks and run them via cron.
Use a managed host
Kinsta and Cloudways pre-configure PHP-FPM timeouts appropriately for WordPress, provide per-site Redis caches, and monitor for slow query patterns. The majority of 504 issues I have seen in the wild are on self-managed VPS setups where timeouts were left at defaults.
Quick reference
| Layer | Directive | Default | Where to change |
|---|---|---|---|
| Nginx | fastcgi_read_timeout | 60s | nginx site config |
| PHP-FPM | request_terminate_timeout | 0 (unlimited) | pool .conf |
| PHP | max_execution_time | 30s | php.ini / .htaccess |
| Apache proxy | ProxyTimeout | 300s | httpd.conf |
| Cloudflare | Route timeout | 100s | Cloudflare dashboard |
Related reading
Frequently Asked Questions
What causes a 504 gateway timeout on WordPress?
How do I fix 504 timeout on WordPress with Nginx?
Is a 504 error a WordPress problem or a server problem?
How do I stop WooCommerce from causing 504 timeouts?
// new_articles
Get notified when new guides drop
Practical WordPress guides from a working agency owner. No filler. Unsubscribe any time.
Was this article helpful?
Thanks for the feedback!