ALB Retornando 502 Bad Gateway: Instâncias Saudáveis, Resposta Inválida
Você abre o console da AWS, confere o target group e todas as instâncias estão Healthy. Mesmo assim, o ALB continua retornando 502 Bad Gateway para os clientes. Esse é um dos cenários mais frustrantes em operações com Application Load Balancer — o health check passa, mas a aplicação real falha silenciosamente na camada de protocolo HTTP.
TL;DR — Diagnóstico Rápido do 502 no ALB
| Causa Raiz | Sinal Observável | Camada |
|---|---|---|
| Resposta HTTP malformada da aplicação | 502 imediato, sem latência | Aplicação |
| Conexão fechada antes da resposta completa | 502 intermitente sob carga | Keep-alive / TCP |
| Timeout de idle no ALB menor que no backend | 502 em requisições longas | Configuração ALB |
| Header HTTP inválido ou tamanho excedido | 502 em rotas específicas | Protocolo HTTP |
| Target desregistrado durante requisição ativa | 502 durante deploy/scale-in | Ciclo de vida |
Como o ALB Processa Respostas — Antes de Depurar
O ALB opera na camada 7 e termina a conexão TCP/TLS com o cliente. Ele então abre uma conexão separada com o target registrado. Isso significa que o ALB valida ativamente a resposta HTTP que recebe do backend antes de repassá-la ao cliente. Se a resposta não for um HTTP válido — cabeçalho malformado, status line inválida, corpo truncado — o ALB descarta a resposta e retorna 502 ao cliente. O health check, por padrão, verifica apenas se o endpoint responde com um código HTTP esperado em um path específico. Ele não valida o comportamento da aplicação sob carga real, com headers reais, ou com payloads reais.
- Cliente → ALB: Conexão TLS terminada no ALB. O cliente nunca fala diretamente com o backend.
- ALB → Target: Nova conexão HTTP/HTTPS estabelecida. O ALB age como proxy reverso.
- Validação da resposta: O ALB inspeciona a resposta HTTP. Qualquer violação de protocolo gera 502.
- Health Check (paralelo): Roda em intervalo configurado, independente do tráfego real. Um target pode passar no health check e ainda assim gerar 502 em requisições reais.
Pense no ALB como um inspetor alfandegário: ele verifica o passaporte (health check) na entrada, mas inspeciona a bagagem (resposta HTTP) em cada passagem. Passaporte válido não garante bagagem aprovada.
Diagnóstico do 502 no ALB: Passo a Passo
Passo 1 — Confirme que o 502 é originado no ALB, não na aplicação
Antes de qualquer coisa, você precisa saber se o 502 está sendo gerado pelo ALB ou se a própria aplicação está retornando 502 para o ALB (o que também resulta em 502 para o cliente). Os access logs do ALB registram o campo target-status-code separado do elb-status-code. Se target-status-code for vazio ou -, o ALB nunca recebeu uma resposta válida do backend — o problema está na camada de protocolo ou conexão, não na lógica da aplicação.
Habilite os access logs do ALB se ainda não estiver ativo:
aws elbv2 modify-load-balancer-attributes \
--load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/meu-alb/1234567890abcdef \
--attributes Key=access_logs.s3.enabled,Value=true \
Key=access_logs.s3.bucket,Value=meu-bucket-logs \
Key=access_logs.s3.prefix,Value=alb-logs
Após alguns minutos, consulte os logs no S3 e filtre por status 502:
aws s3 cp s3://meu-bucket-logs/alb-logs/ ./alb-logs/ --recursive --exclude "*" --include "*.log.gz"
zcat alb-logs/*.log.gz | awk '$9 == 502 {print $9, $13, $14}' | head -50
Os campos impressos são: elb-status-code, target-status-code e target-processing-time. Se target-status-code for -, pule para o Passo 3. Se for um código válido como 502, a aplicação está gerando o erro — investigue nos logs da aplicação.
Passo 2 — Verifique o timeout de idle do ALB versus o keep-alive do backend
Esse é o erro de diagnóstico mais comum que vejo em produção. O timeout de idle padrão do ALB é 60 segundos. Se o servidor de aplicação (Nginx, Node.js, Gunicorn, etc.) fechar a conexão keep-alive antes desse tempo, o ALB pode tentar reutilizar uma conexão já fechada pelo backend — e quando isso acontece no meio de uma requisição, o resultado é 502. O problema é intermitente e piora sob carga, o que leva equipes a culpar a aplicação ou a infraestrutura de rede.
Consulte o timeout atual do ALB:
aws elbv2 describe-load-balancer-attributes \
--load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/meu-alb/1234567890abcdef \
--query 'Attributes[?Key==`idle_timeout.timeout_seconds`]'
A regra operacional: configure o keepalive_timeout do servidor de aplicação para um valor maior que o idle timeout do ALB. Se o ALB está em 60s, configure o backend para 65s ou mais. Isso garante que o ALB sempre feche a conexão antes do backend.
Para ajustar o idle timeout do ALB:
aws elbv2 modify-load-balancer-attributes \
--load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/meu-alb/1234567890abcdef \
--attributes Key=idle_timeout.timeout_seconds,Value=60
Passo 3 — Inspecione a resposta HTTP diretamente do backend
O health check do ALB não reproduz uma requisição real. Para replicar o que o ALB enxerga, conecte-se diretamente à instância e faça uma requisição HTTP crua. Isso expõe headers malformados, status lines inválidas, ou respostas sem Content-Length e sem Transfer-Encoding — todas causas documentadas de 502 no ALB.
# Substitua 10.0.1.50 pelo IP privado da instância e 8080 pela porta do target group
curl -v --http1.1 -H 'Host: meu-dominio.com' http://10.0.1.50:8080/caminho-que-gera-502 2>&1 | head -60
Procure por:
- Status line com formato inválido (ex:
HTTP/1.1200 OKsem espaço) - Headers com caracteres de controle ou encoding inválido
- Resposta sem
Content-Lengthe semTransfer-Encoding: chunkedem conexões persistentes - Corpo da resposta truncado antes do fim declarado pelo
Content-Length
Passo 4 — Verifique o protocolo configurado no target group
Se o target group está configurado para HTTPS mas a aplicação responde em HTTP (ou vice-versa), o ALB recebe dados que não consegue interpretar como HTTP válido e retorna 502. Esse erro é especialmente comum após migrações ou mudanças de configuração de TLS no backend.
aws elbv2 describe-target-groups \
--target-group-arns arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/meu-tg/abcdef1234567890 \
--query 'TargetGroups[0].{Protocol:Protocol,Port:Port,HealthCheckProtocol:HealthCheckProtocol}'
Confirme que o protocolo e a porta retornados correspondem ao que a aplicação realmente escuta. Um target group HTTPS apontando para uma aplicação que responde em HTTP vai gerar 502 consistente — e o health check pode estar usando HTTP em um path diferente, mascarando o problema.
Passo 5 — Identifique 502s durante deploys e scale-in
Targets sendo desregistrados durante requisições ativas geram 502. O ALB encaminha a requisição, o target começa a processar, e então a conexão é encerrada pelo processo de desregistro. O atributo deregistration_delay.timeout_seconds controla quanto tempo o ALB aguarda conexões ativas drenarem antes de remover o target definitivamente.
aws elbv2 describe-target-group-attributes \
--target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/meu-tg/abcdef1234567890 \
--query 'Attributes[?Key==`deregistration_delay.timeout_seconds`]'
Se o valor estiver muito baixo (ex: 5s) e suas requisições levam mais tempo para completar, aumente para um valor que cubra o tempo máximo de processamento de uma requisição típica. O padrão é 300 segundos.
aws elbv2 modify-target-group-attributes \
--target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/meu-tg/abcdef1234567890 \
--attributes Key=deregistration_delay.timeout_seconds,Value=30
- Resposta inválida: ALB recebe dados que violam o protocolo HTTP — 502 imediato.
- Conexão fechada prematuramente: Backend fecha keep-alive antes do ALB — 502 intermitente.
- Timeout de processamento: Requisição excede o idle timeout configurado — 502 em operações longas.
- Desregistro durante requisição: Target removido enquanto processa — 502 durante deploys.
O Diagnóstico que Nos Custou Duas Horas
Em um ambiente de produção com Node.js atrás de um ALB, começamos a ver 502s esporádicos sob carga moderada. Os logs da aplicação não mostravam nada — nenhum erro, nenhum crash, nenhum timeout. O health check estava verde. Métricas de CPU e memória normais.
A hipótese inicial foi Security Group bloqueando tráfego intermitentemente. Verificamos as regras, tudo correto. Depois suspeitamos de um bug na aplicação em uma rota específica. Não era — o 502 aparecia em rotas diferentes a cada vez.
Quando finalmente olhamos os access logs do ALB com atenção, o campo target-status-code estava vazio (-) em todos os 502s. O ALB nunca recebia resposta. O target-processing-time era exatamente -1 — conexão recusada ou fechada antes da resposta.
A causa: o servidor HTTP do Node.js tinha server.keepAliveTimeout configurado para 5 segundos (padrão em versões mais antigas do Node.js). O ALB, com idle timeout de 60 segundos, mantinha conexões abertas por até 60s e tentava reutilizá-las. Quando o Node.js fechava a conexão após 5s de idle e o ALB tentava enviar uma nova requisição por essa conexão já fechada, o resultado era 502.
A correção foi adicionar duas linhas no código de inicialização do servidor:
// Node.js — configure keepAliveTimeout maior que o idle timeout do ALB
server.keepAliveTimeout = 65000; // 65 segundos
server.headersTimeout = 66000; // deve ser maior que keepAliveTimeout
O headersTimeout precisa ser maior que o keepAliveTimeout — caso contrário, o Node.js pode fechar a conexão antes de terminar de ler os headers de uma nova requisição que chegou exatamente no limite do timeout.
IAM Mínimo para Acesso aos Logs do ALB
Para que a equipe de operações consiga acessar os access logs sem permissões excessivas:
🔽 Clique para expandir — Política IAM mínima para logs do ALB
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DescribeALBAttributes",
"Effect": "Allow",
"Action": [
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DescribeLoadBalancerAttributes",
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:DescribeTargetGroupAttributes",
"elasticloadbalancing:DescribeTargetHealth"
],
"Resource": "*"
},
{
"Sid": "ModifyALBAttributes",
"Effect": "Allow",
"Action": [
"elasticloadbalancing:ModifyLoadBalancerAttributes",
"elasticloadbalancing:ModifyTargetGroupAttributes"
],
"Resource": [
"arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/meu-alb/*",
"arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/meu-tg/*"
]
},
{
"Sid": "ReadALBLogsFromS3",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::meu-bucket-logs",
"arn:aws:s3:::meu-bucket-logs/alb-logs/*"
]
}
]
}
Note que as ações Describe* do Elastic Load Balancing exigem "Resource": "*" — não suportam restrição por ARN específico conforme documentado no Service Authorization Reference da AWS.
Métricas do CloudWatch para Monitorar 502s no ALB
Configure um alarme na métrica HTTPCode_ELB_502_Count no namespace AWS/ApplicationELB para detecção proativa:
aws cloudwatch put-metric-alarm \
--alarm-name 'ALB-502-Errors' \
--namespace AWS/ApplicationELB \
--metric-name HTTPCode_ELB_502_Count \
--dimensions Name=LoadBalancer,Value=app/meu-alb/1234567890abcdef \
--statistic Sum \
--period 60 \
--evaluation-periods 2 \
--threshold 5 \
--comparison-operator GreaterThanOrEqualToThreshold \
--alarm-actions arn:aws:sns:us-east-1:123456789012:meu-topico-alertas \
--treat-missing-data notBreaching
Complemente com HTTPCode_Target_5XX_Count para distinguir erros originados no backend dos gerados pelo próprio ALB. Se HTTPCode_ELB_502_Count sobe mas HTTPCode_Target_5XX_Count permanece baixo, o problema está na camada de protocolo ou conexão — não na lógica da aplicação.
Conclusão e Próximos Passos com ALB 502
Um 502 no ALB com targets saudáveis quase sempre aponta para uma das três camadas: protocolo HTTP inválido, desalinhamento de timeout de conexão, ou ciclo de vida de target durante deploys. Os access logs do ALB são o ponto de partida obrigatório — especificamente os campos target-status-code e target-processing-time, que revelam se o ALB chegou a receber alguma resposta do backend.
- Consulte a documentação oficial de access logs do ALB para o formato completo dos campos de log.
- Revise os códigos de erro do ALB para entender as causas documentadas de cada código 5xx.
- Se o problema for intermitente e difícil de reproduzir, considere habilitar o AWS X-Ray no ALB para rastreamento distribuído de requisições.
Glossário
| Termo | Definição Operacional |
|---|---|
| Target Group | Agrupamento lógico de backends (instâncias EC2, IPs, funções Lambda) que recebem tráfego do ALB. Cada target group tem seu próprio health check e protocolo configurado. |
| Idle Timeout | Tempo máximo que o ALB mantém uma conexão aberta sem atividade. Após esse período, a conexão é encerrada pelo ALB. Padrão: 60 segundos. |
| Keep-Alive | Mecanismo HTTP que permite reutilizar uma conexão TCP para múltiplas requisições, evitando o overhead de estabelecer nova conexão a cada request. |
| Deregistration Delay | Período que o ALB aguarda para drenar conexões ativas de um target antes de removê-lo definitivamente do target group. |
| HTTPCode_ELB_502_Count | Métrica do CloudWatch que conta respostas 502 geradas pelo próprio ALB — distintas de 502s retornados pelos backends. |
Comentários
Postar um comentário