Common causes
- A PHP script runs longer than fastcgi_read_timeout (60s by default)
- Slow or locked MySQL queries
- External API calls with no timeout
- All PHP-FPM workers busy, so new requests wait in the queue
- 'while connecting to upstream' means the backend host or port is unreachable
- Heavy reports, imports or backups run in a web request
How to fix it
- Note which phase timed out. 'while connecting to upstream' means Nginx could not reach the backend; check it is running and reachable. 'while reading response header' means the backend accepted but answered too slowly.
- Find the slow code. Enable the PHP-FPM slow log: request_slowlog_timeout = 10s and slowlog = /var/log/php8.3-fpm-slow.log in the pool config. Restart PHP-FPM and read the stack traces.
- Check the database. Run SHOW FULL PROCESSLIST; in MySQL during the slowdown. Long-running or locked queries point to missing indexes or lock contention.
- Check worker capacity. Look in the PHP-FPM log for 'server reached pm.max_children'. If you see it, requests are waiting for a free worker; tune pm.max_children to your RAM.
- Raise the timeout for known long tasks. Set fastcgi_read_timeout 300s; (or proxy_read_timeout 300s; for proxy_pass) in the specific location, and match PHP's max_execution_time and request_terminate_timeout.
- Move heavy work to the background. Run imports and exports with a queue or cron job so the HTTP request returns quickly.
Raise read timeouts for one slow endpoint only
location = /wp-admin/admin-ajax.php {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_read_timeout 180s;
}
location /api/report {
proxy_pass http://127.0.0.1:3000;
proxy_read_timeout 180s;
} How to stop it happening again
- Profile slow endpoints before raising timeouts
- Index columns used in WHERE and JOIN clauses
- Set timeouts on all outbound HTTP calls
- Monitor PHP-FPM worker usage