You’ve bumped max_execution_time to 300 seconds in PHP, but your app still throws 504 Gateway Timeout after about a minute. Sound familiar?
The problem isn’t PHP. It’s the timeout chain — every layer between the browser and your code has its own timeout, and they all need to agree.
The typical Docker stack
Browser → Nginx (reverse proxy) → PHP-FPM → PHP script
Each layer has a default timeout:
| Layer | Setting | Default |
|---|---|---|
| Nginx | fastcgi_read_timeout |
60s |
| PHP-FPM | request_terminate_timeout |
0 (unlimited) |
| PHP | max_execution_time |
30s |
If you only change max_execution_time to 300s, PHP is happy to run that long — but Nginx kills the connection at 60 seconds because nobody told it to wait longer. You get a 504, PHP keeps running in the background (wasting resources), and your logs show no PHP errors because PHP didn’t fail.
The fix: align the entire chain
php.ini overrides:
max_execution_time = 300
max_input_time = 300
memory_limit = 512M
Nginx site config (inside your location ~ \.php$ block):
location ~ \.php$ {
fastcgi_pass php:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# Match PHP's max_execution_time
fastcgi_read_timeout 300s;
fastcgi_send_timeout 300s;
fastcgi_connect_timeout 60s;
# Prevent 504 on large responses
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
fastcgi_max_temp_file_size 0;
}
If you have a CDN or load balancer in front (Cloudflare, AWS ALB), add that to the chain too:
CDN (100s) → Nginx (300s) → PHP-FPM (0) → PHP (300s)
↑ This is the actual limit
The debugging trick
When you get a 504, test from the inside out:
- Hit PHP-FPM directly (bypass Nginx): Does the request complete?
- Hit Nginx locally (bypass CDN): Does it time out?
- Hit the public URL: Does the CDN add its own timeout?
This tells you exactly which layer is killing the request. Compare your local dev config against production — often the mismatch is the missing fastcgi_read_timeout that production has but your Docker setup doesn’t.
Rule of thumb: every layer’s timeout should be ≥ the layer below it. If PHP allows 300s, Nginx must allow at least 300s. If Nginx allows 300s, the CDN must allow at least 300s. One weak link and the whole chain breaks.

Leave a Reply