← Voltar aos artigos

php-fpm · servidor · timeout · nginx · produção

504 Gateway Timeout no PHP-FPM: qual dos três timeouts é o seu

Publicado em 10/08/2026 · 10 min de leitura

Quando uma página demora demais e volta 504 Gateway Timeout, não existe "o timeout do PHP": existem três relógios independentes correndo ao mesmo tempo, cada um pertencendo a uma camada diferente — max_execution_time (PHP), request_terminate_timeout (PHP-FPM) e fastcgi_read_timeout (nginx). Vence o menor. É exatamente por isso que aumentar um só não resolve: você levanta o teto de um relógio e continua batendo no outro, que ficou para trás. A boa notícia é que cada um deixa uma mensagem diferente — dá para saber em segundos qual disparou, em vez de mexer no escuro.

Quem é dono de cada relógio

Para entender por que são três, siga o caminho de uma requisição. O nginx recebe o pedido do navegador e o repassa ao PHP-FPM; ele então fica esperando a resposta — e tem um limite de paciência (fastcgi_read_timeout). O master do FPM entrega o pedido a um worker e fica vigiando quanto tempo aquele worker leva (request_terminate_timeout). Dentro do worker, o próprio PHP cronometra a execução do seu script (max_execution_time).

São três vigias observando a mesma requisição de pontos diferentes. Como cada um age sozinho ao estourar, o primeiro a chegar no limite define o que você vê:

Relógio Onde se configura Padrão típico O que faz ao estourar
max_execution_time php.ini / pool 30 s Erro fatal no script; o PHP devolve a resposta
request_terminate_timeout pool do FPM 0 (desligado) Mata o worker e registra no log do FPM
fastcgi_read_timeout nginx 60 s Desiste de esperar e devolve 504 ao navegador

Repare numa assimetria importante: só o do nginx produz o 504 na tela do usuário. Os outros dois cortam do lado do PHP — e o que o visitante vê depende de qual foi.

Identificando o culpado pela mensagem

Cada relógio escreve num lugar diferente, com um texto característico. Este é o atalho do diagnóstico:

Se foi o PHP (max_execution_time)

O script morre com erro fatal e a mensagem vai para o log de erros do PHP. O usuário costuma receber uma página de erro (ou branca), não um 504.

PHP Fatal error:  Maximum execution time of 30 seconds exceeded
in /var/www/relatorio.php on line 42

Se foi o FPM (request_terminate_timeout)

O master mata o worker e registra no log do FPM — com o script e o tempo real, o que é ótimo para diagnóstico.

WARNING: [pool www] child 2841, script '/var/www/relatorio.php'
(request: "GET /relatorio.php") execution timed out (35.002471 sec), terminating

Se foi o nginx (fastcgi_read_timeout)

O nginx desiste de esperar e devolve 504. A marca registrada está no error.log do nginx.

upstream timed out (110: Connection timed out)
while reading response header from upstream

O detalhe que fecha o diagnóstico

Se você vê o 504 no navegador e o log do nginx tem upstream timed out, mas o log do PHP está vazio, a conclusão é direta: o nginx cortou primeiro. O PHP nem chegou perto do limite dele — pode até ter continuado rodando depois.

504 + log do PHP vazio  →  o relógio do nginx é o menor

A ordem certa: PHP < FPM < nginx

A recomendação é escalonar os três valores de dentro para fora, com folga entre eles:

; PHP (php.ini do FPM, ou php_admin_value no pool)
max_execution_time = 30

; pool do FPM (ex.: /etc/php/8.3/pool.d/www.conf)
request_terminate_timeout = 35

; nginx (no location do PHP)
fastcgi_read_timeout = 60;

O porquê é prático: você quer que o relógio mais interno dispare primeiro. Se o PHP corta antes, você ganha uma mensagem de erro com arquivo e linha — o diagnóstico ideal. Se quem corta é o FPM, você ao menos sabe qual script travou. Se quem corta é o nginx, você fica com um 504 anônimo e um worker possivelmente ainda ocupado. O request_terminate_timeout funciona como uma rede de segurança logo acima do PHP: ele pega os casos em que o relógio do PHP não conseguiu agir — e a próxima seção mostra que esses casos são comuns.

evite
; "está dando 504? aumenta o do PHP"
max_execution_time = 300

; ...e os outros dois ficaram como estavam:
; request_terminate_timeout = 0   (desligado)
; fastcgi_read_timeout = 60s      ← o nginx corta em 60s do mesmo jeito
; Resultado: continua 504, e agora o worker fica preso 300s.
prefira
; escalonado de dentro para fora, com folga
max_execution_time = 30          ; PHP  — erro com arquivo e linha
request_terminate_timeout = 35   ; FPM  — rede de segurança
fastcgi_read_timeout = 60;       ; nginx — o mais tolerante

; e o principal: investigar POR QUE leva 30s (slowlog)

O relógio do PHP tem um ponto cego

Esta é a parte que explica a maioria dos casos "aumentei tudo e continua". O max_execution_time não conta o tempo gasto fora do script — chamadas de sistema, esperas de rede, consultas ao banco. Ele mede o tempo de execução do PHP em si. Dá para provar em duas linhas:

// com max_execution_time = 1
sleep(3);
echo "sobreviveu";   // imprime! o sleep não conta para o relógio do PHP

Um laço de CPU nas mesmas condições morre na hora com Maximum execution time of 1 second exceeded. O sleep(3), não. Troque o sleep pelo que acontece no mundo real — uma query que demora 90 segundos, um curl para uma API que não responde, um file_get_contents remoto — e o efeito é o mesmo: o script fica parado esperando, e o relógio do PHP praticamente não anda.

Consequências diretas:

Você tem max_execution_time = 300 e uma página que consulta um relatório pesado no banco. Ela devolve 504 aos 60 segundos e o log do PHP está vazio. Qual é a explicação?

Ver resposta

Resposta certa: O fastcgi_read_timeout do nginx (60s) é o menor dos três e cortou primeiro

O 504 aos 60 segundos entrega o culpado: 60s é o padrão do fastcgi_read_timeout. O nginx desistiu de esperar e respondeu ao navegador — por isso o log do PHP está limpo, já que o script nem foi interrompido (provavelmente continuou rodando). Aumentar o max_execution_time para 300 não teve efeito nenhum, e nem teria: além de o nginx ser o menor relógio, o tempo de espera pelo banco quase não conta para o relógio do PHP. Confirme no error.log do nginx procurando por upstream timed out.

Aumentar timeout raramente é a solução

Ajustar os três valores conserta o sintoma e é necessário para não cortar no lugar errado — mas uma página que leva mais de 30 segundos tem um problema de arquitetura, não de configuração. Enquanto ela roda, um worker do FPM fica inteiramente ocupado (o PHP-FPM não é assíncrono): 10 requisições dessas em paralelo consomem 10 workers e podem derrubar o site inteiro por falta de workers livres.

O caminho certo, em ordem de esforço:

  1. Descubra onde o tempo vai. Ligue o slowlog do FPM (request_slowlog_timeout + slowlog): ele grava o backtrace do script travado, com função e linha.
  2. Ataque a causa. Quase sempre é índice faltando, N+1 de consultas, ou chamada externa sem timeout.
  3. Tire o trabalho pesado do request. Relatórios, e-mails em massa, processamento de arquivo e integrações lentas devem rodar numa fila ou num comando de CLI (onde o max_execution_time é 0 por padrão, e ninguém está esperando na frente do navegador). O usuário recebe "estamos processando" na hora e é avisado quando terminar.

Resumo de decisão

Sintoma Culpado O que fazer
504 + upstream timed out no log do nginx fastcgi_read_timeout É o menor relógio — alinhe os três
execution timed out (N sec), terminating no log do FPM request_terminate_timeout Rede de segurança funcionou; investigue o script citado
Maximum execution time of N seconds exceeded max_execution_time Melhor cenário: você tem arquivo e linha
504 e nenhum log do PHP nginx cortou primeiro Não procure no log da aplicação
Script preso em query/API e nada o mata Ponto cego do relógio do PHP Defina request_terminate_timeout e timeout nas chamadas externas
A página legitimamente leva minutos Arquitetura Fila ou CLI, não timeout maior

Em uma frase: descubra qual relógio disparou pela mensagem, escalone os três de dentro para fora — e depois trate o motivo de a página estar lenta, porque timeout maior só adia o problema segurando um worker por mais tempo.

no manualConfiguration

A página de configuração do FPM documenta as diretivas de pool — request_terminate_timeout, request_slowlog_timeout, slowlog e companhia — com o significado exato de cada uma e as restrições entre elas.

estudar na árvore →
no manualWhere a configuration setting may be set

Entender onde cada diretiva pode ser definida (php.ini, pool, .htaccess, ini_set em runtime) evita o clássico "mudei o valor e não pegou" — max_execution_time é um caso típico.

estudar na árvore →

Leve o estudo além do artigo

O manual inteiro do PHP como uma jornada: progresso por tópico, anotações e leitura embutida.