Tutoriais de VPS
Como resolver 502 / 504 Bad Gateway em uma VPS (2026): checklist de diagnóstico de Nginx, PHP-FPM, Gunicorn e timeout de upstream
Quando um site em VPS de repente mostra 502 Bad Gateway ou 504 Gateway Timeout, geralmente não é um problema de domínio, mas sim o Nginx que não consegue conectar ao backend, PHP-FPM/Gunicorn que caiu, permissões erradas no socket Unix, pool de processos esgotado ou resposta do upstream demorando demais. Este artigo diagnostica camada por camada por meio de logs, portas, sockets, status dos serviços, timeouts e uso de recursos.

502 e 504 são os dois erros mais comuns em hospedagem de sites em VPS, e também os que mais causam pânico.
Você abre o site no navegador e só vê:
502 Bad Gateway
504 Gateway Timeout
Muitas pessoas pensam logo que o domínio quebrou, o certificado quebrou ou o servidor foi bloqueado. Na verdade, na maioria das vezes não é nada disso. 502/504 normalmente significa: o Nginx continua vivo, mas a aplicação por trás dele não está respondendo corretamente.
Dá para entender assim: o porteiro continua na porta, mas ninguém na cozinha está aceitando pedidos, ou a cozinha está aceitando pedidos devagar demais.
Este artigo segue uma ordem prática: primeiro confirmar a causa pelos logs do Nginx, depois verificar PHP-FPM, Gunicorn, Node e upstreams Docker, e por fim olhar timeouts, pools de processos, memória e disco. Não reinstale o ambiente de cara e não se limite a reiniciar a VPS inteira.
Em resumo:
- 502 Bad Gateway: o Nginx falhou ao conectar ao backend, ou o backend retornou uma resposta inválida
- 504 Gateway Timeout: o Nginx conectou, mas esperou demais pela resposta do backend
Cenários comuns:
| Erro | Causas comuns |
|---|---|
| 502 | PHP-FPM não iniciado, caminho do socket errado, porta sem escuta, processo do backend caiu |
| 502 | Permissões do socket Unix incorretas, Nginx não consegue acessar o socket do PHP-FPM |
| 502 | Contêiner Docker caiu, porta do proxy reverso errada |
| 504 | PHP/aplicação executa devagar demais, banco de dados travado, API bloqueada |
| 504 | proxy_read_timeout / fastcgi_read_timeout do Nginx curtos demais |
| 504 | Pool de processos do backend cheio, requisições na fila por tempo demais |
Portanto, a direção do diagnóstico não é "o site não abre", mas "onde a cadeia do proxy front-end até o backend está quebrada".
Não adivinhe. Olhe os logs primeiro:
sudo tail -n 100 /var/log/nginx/error.log
Ver em tempo real:
sudo tail -f /var/log/nginx/error.log
Depois atualize a página e veja qual é o novo erro.
Tipos comuns:
connect() failed (111: Connection refused) while connecting to upstream
Significa que o Nginx tentou conectar à porta ou ao socket do backend e a conexão foi recusada. O backend pode não estar iniciado, ou a porta está errada.
connect() to unix:/run/php/php8.2-fpm.sock failed (13: Permission denied)
Indica problema de permissão do socket.
upstream timed out (110: Connection timed out) while reading response header from upstream
Significa que o backend responde devagar demais, mais voltado a 504/timeout.
no live upstreams while connecting to upstream
Significa que não há backend disponível no grupo upstream.
Se você entender essa linha, basicamente já sabe onde verificar em seguida.
Se você alterou recentemente a configuração do site, verifique o Nginx primeiro:
sudo nginx -t
Se a configuração estiver correta, recarregue:
sudo systemctl reload nginx
Não execute restart diretamente. nginx -t consegue detectar erros de sintaxe na configuração antes, e o reload é mais suave que o restart.
Depois verifique o status:
systemctl status nginx --no-pager
journalctl -u nginx --since "30 minutes ago" --no-pager
A documentação oficial do Nginx também distingue claramente os níveis de error_log e os logs de depuração. Para diagnóstico comum, o error log já basta; logs de depuração servem para problemas complexos, então não os ative logo de início, senão os logs vão crescer muito rápido.
Em sites PHP como WordPress, Typecho, Laravel e ThinkPHP, um 502 provavelmente está relacionado ao PHP-FPM.
Primeiro veja o nome do serviço. Em sistemas diferentes pode ser:
systemctl list-units | grep fpm
Normalmente é:
php8.1-fpm
php8.2-fpm
php8.3-fpm
Verifique o status:
systemctl status php8.2-fpm --no-pager
journalctl -u php8.2-fpm --since "30 minutes ago" --no-pager
Se não estiver iniciado:
sudo systemctl restart php8.2-fpm
Mas não pare depois de reiniciar. Continue olhando os logs para confirmar se é erro de configuração, memória insuficiente ou falha ao carregar extensão.
O Nginx e o PHP-FPM normalmente se comunicam por um socket Unix ou uma porta TCP.
Primeiro encontre o que está escrito no Nginx:
sudo grep -R "fastcgi_pass" /etc/nginx/sites-enabled /etc/nginx/conf.d
Você pode ver:
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
Então confirme se o socket existe:
ls -l /run/php/
Se o Nginx está configurado com php8.1-fpm.sock, mas o sistema na verdade só tem php8.2-fpm.sock, isso é um 502 típico.
Se o Nginx está configurado com TCP:
fastcgi_pass 127.0.0.1:9000;
Verifique a porta:
ss -lntp | grep 9000
Na documentação oficial de FastCGI do Nginx, fastcgi_pass é a diretiva chave que define para qual backend FastCGI a requisição é entregue. Se o caminho ou a porta estiverem errados, não adianta o Nginx estar saudável.
Se no log aparecer:
Permission denied while connecting to upstream
Primeiro veja as permissões do socket:
ls -l /run/php/php8.2-fpm.sock
Depois veja o usuário com que o Nginx roda:
ps -o user,group,cmd -C nginx
A configuração do pool do PHP-FPM normalmente inclui estes itens:
listen = /run/php/php8.2-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
Locais comuns dos arquivos de configuração:
/etc/php/8.2/fpm/pool.d/www.conf
Depois de alterar, verifique primeiro e depois reinicie o PHP-FPM:
sudo php-fpm8.2 -t
sudo systemctl restart php8.2-fpm
sudo systemctl reload nginx
Os comandos podem variar um pouco entre distribuições. Se php-fpm8.2 -t não existir, você pode usar:
php-fpm8.2 -tt
Ou verificar o nome do binário do PHP-FPM no sistema atual.
Se o tráfego do site aumentar, ou alguma requisição PHP travar, o pool de processos do PHP-FPM pode se esgotar.
Nos logs pode aparecer:
server reached pm.max_children setting
Primeiro verifique os logs do PHP-FPM:
journalctl -u php8.2-fpm --since "1 hour ago" --no-pager
Depois veja a configuração do pool:
sudo grep -E "^pm\.|^request_" /etc/php/8.2/fpm/pool.d/www.conf
Itens comuns:
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 5
request_terminate_timeout = 60s
A documentação oficial de configuração do PHP-FPM do PHP traz parâmetros como pm.max_children, pm.max_requests, request_terminate_timeout, slowlog e request_slowlog_timeout. Não fique só aumentando pm.max_children sem limite, pois ele consome memória.
Uma estimativa aproximada da memória dos processos PHP pode ser feita assim:
ps -ylC php-fpm8.2 --sort:rss
Se cada processo filho PHP usa 80MB e a máquina tem só 1GB de memória, definir pm.max_children como 50 é muito perigoso. Pode não resolver o 502 e ainda levar a VPS ao OOM.
O PHP-FPM oferece suporte oficial a uma página de status, onde é possível ver o estado do pool de processos, como active processes, listen queue, max children reached e slow requests.
Adicione na configuração do pool:
pm.status_path = /fpm-status
ping.path = /fpm-ping
No Nginx, permita apenas acesso local:
location ~ ^/(fpm-status|fpm-ping)$ {
access_log off;
allow 127.0.0.1;
deny all;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
Depois do reload, teste:
curl http://127.0.0.1/fpm-status
curl http://127.0.0.1/fpm-status?json
curl http://127.0.0.1/fpm-ping
Se listen queue frequentemente não for 0, ou max children reached crescer, significa que o PHP-FPM não está dando conta.
Se você usa Django, Flask ou FastAPI, o backend pode ser Gunicorn ou Uvicorn.
Primeiro veja a configuração de upstream do Nginx:
sudo grep -R "proxy_pass" /etc/nginx/sites-enabled /etc/nginx/conf.d
Se for:
proxy_pass http://127.0.0.1:8000;
Verifique a porta:
ss -lntp | grep 8000
Verifique o serviço:
systemctl status gunicorn --no-pager
journalctl -u gunicorn --since "30 minutes ago" --no-pager
Se não tiver certeza do nome do serviço:
systemctl list-units | grep -Ei "gunicorn|uvicorn|app"
ps -ef | grep -Ei "gunicorn|uvicorn|python"
Se o Nginx consegue conectar à porta mas retorna 504, normalmente é preciso verificar os logs da aplicação, as conexões com o banco de dados, consultas lentas e timeouts de APIs de terceiros.
Aplicações Node costumam usar proxy reverso do Nginx para uma porta local.
Verifique o Nginx:
sudo grep -R "proxy_pass" /etc/nginx/sites-enabled /etc/nginx/conf.d
Verifique a porta:
ss -lntp | grep -E ':3000|:8080|:8000'
Se usar PM2:
pm2 status
pm2 logs --lines 100
Causas comuns de 502:
- Processo Node caiu
- Porta mudou e o Nginx não foi atualizado
- Variáveis de ambiente não carregadas
- Aplicação inicia devagar ou trava
- Memória estourou e o sistema matou o processo
Verifique OOM:
journalctl -k --since "2 hours ago" | grep -Ei "out of memory|killed process"
Ao rodar um site em Docker, o Nginx pode estar no host ou em um contêiner.
Primeiro veja o status do contêiner:
docker ps
docker compose ps
Veja os logs:
docker logs 容器名 --tail 100
docker compose logs --tail 100 服务名
Veja o mapeamento de portas:
docker ps --format 'table {{.Names}}\t{{.Ports}}'
Se o Nginx estiver configurado com:
proxy_pass http://127.0.0.1:3000;
mas o Docker não mapeou a 3000 para o host, ou o serviço dentro do contêiner escuta em 127.0.0.1 em vez de 0.0.0.0, ocorre 502.
Teste dentro do contêiner:
docker exec -it 容器名 sh
curl -I http://127.0.0.1:3000
Teste no host:
curl -I http://127.0.0.1:3000
Esses dois resultados ajudam a determinar se o problema está dentro do contêiner ou no mapeamento de portas do contêiner para o host.
Se o erro for 504, o backend normalmente não está totalmente caído, mas sim lento demais.
Parâmetros comuns no Nginx:
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
fastcgi_connect_timeout 60s;
fastcgi_send_timeout 60s;
fastcgi_read_timeout 60s;
A documentação oficial do Nginx traz parâmetros como fastcgi_read_timeout e fastcgi_connect_timeout. Mas não mude o timeout para 600 segundos só porque viu um 504.
Se uma página leva 5 minutos para rodar, o problema real pode ser:
- Consultas lentas no banco de dados
- API externa travada
- Upload de arquivo grande demais
- Tarefas PHP que não deveriam rodar dentro de uma requisição web
- Fila não separada
- CPU / IO saturados
Verifique os recursos primeiro:
top
free -m
df -h
df -i
iostat -xz 1
Se não houver iostat:
sudo apt install sysstat
Depois veja os logs da aplicação e as consultas lentas do banco de dados. Ajustar o timeout só alivia a dor, não cura a doença.
Quando um site dá 502/504, eu normalmente sigo esta ordem:
curl -I https://example.com
sudo tail -n 100 /var/log/nginx/error.log
sudo nginx -t
systemctl status nginx --no-pager
systemctl list-units | grep -Ei "php|fpm|gunicorn|uvicorn|docker"
ss -lntp
Se for PHP:
systemctl status php8.2-fpm --no-pager
journalctl -u php8.2-fpm --since "30 minutes ago" --no-pager
sudo grep -R "fastcgi_pass" /etc/nginx/sites-enabled /etc/nginx/conf.d
ls -l /run/php/
Se for aplicação com proxy reverso:
sudo grep -R "proxy_pass" /etc/nginx/sites-enabled /etc/nginx/conf.d
ss -lntp | grep -E ':3000|:8000|:8080'
journalctl -u gunicorn --since "30 minutes ago" --no-pager
pm2 status
Se for Docker:
docker ps
docker compose ps
docker compose logs --tail 100
Primeiro localize o ponto de quebra da cadeia, depois decida se reinicia o serviço, altera o socket, ajusta o pool de processos ou investiga requisições lentas.
Reiniciar o Nginx, o PHP-FPM ou o serviço da aplicação às vezes recupera, mas pode apenas apagar a cena.
Uma ordem melhor é:
- Capturar o error.log do Nginx
- Registrar o status do serviço do backend
- Ver se a porta/socket existe
- Ver se há OOM, disco cheio, inodes cheios
- Depois decidir reload/restart
Se você apenas executar às cegas:
sudo reboot
A máquina sobe e o site volta, mas você também não sabe qual era a causa raiz. Na próxima hora de pico vai acontecer de novo.
- Confirmado se é 502 ou 504
- Verificado
/var/log/nginx/error.log - Executado
nginx -t - Confirmado que o serviço Nginx está normal
- Em sites PHP, verificado o status e os logs do PHP-FPM
- Conferido se o socket/porta de
fastcgi_passexiste - Verificado as permissões do socket e os usuários do Nginx/PHP-FPM
- Verificado se
pm.max_childrenestá cheio - Em aplicações Python/Node, confirmado o processo e a porta de escuta
- Em aplicações Docker, confirmado o status do contêiner, os logs e o mapeamento de portas
- Em 504, verificado requisições lentas, banco de dados, APIs externas, CPU/IO
- Não dependeu apenas de reiniciar para mascarar a causa raiz
A ideia mais importante sobre 502/504 é: o Nginx estar acessível não significa que o site esteja acessível. O que você precisa verificar é a cadeia do Nginx até o backend: não conecta, sem permissão, backend caiu, fila cheia ou resposta lenta demais. Se você separar essas camadas e verificar, o problema normalmente chega rápido a um serviço específico.