ALB Retourne 502 Bad Gateway : Instances Healthy mais Réponse Invalide
Vous regardez votre Target Group et toutes les instances affichent Healthy — pourtant l'ALB retourne des 502 à vos utilisateurs. C'est l'un des scénarios les plus frustrants en production : le health check passe, mais le trafic réel échoue. Le problème n'est pas la disponibilité de l'instance, c'est la nature de la réponse HTTP que l'application renvoie à l'ALB.
TL;DR — Diagnostic Rapide ALB 502
| Cause | Signal Observable | Couche |
|---|---|---|
| Réponse HTTP invalide ou malformée | access_log ALB : 502, target_status_code: - | Application |
| Connexion fermée prématurément par la cible | target_status_code: - ou vide | TCP/Application |
| Désalignement keep-alive (timeout) | 502 intermittents, pas de log d'erreur applicatif | Connexion |
| Réponse trop lente (idle timeout dépassé) | target_processing_time proche de 60s | Timing |
| Mauvais protocole cible (HTTP vs HTTPS) | Erreur TLS dans les logs cible | Protocole |
Comment l'ALB Traite une Requête — Mécanique Interne
Avant de diagnostiquer, il faut comprendre pourquoi un 502 apparaît alors que le health check est vert. Le health check ALB est une sonde périodique légère — souvent un GET sur /health — qui valide uniquement que l'instance répond avec un code HTTP attendu. Il ne valide pas que l'application peut traiter des requêtes complexes, maintenir des connexions longues, ou produire des réponses HTTP syntaxiquement valides sous charge.
Quand l'ALB reçoit une requête client, il établit une connexion TCP séparée vers la cible, transmet la requête, puis attend une réponse HTTP complète et valide. Si la cible ferme la connexion sans envoyer de réponse complète, envoie des headers malformés, ou dépasse l'idle timeout, l'ALB génère un 502 — indépendamment du statut du health check.
- Client → ALB : requête HTTP entrante sur le listener (port 80/443).
- ALB → Cible : nouvelle connexion TCP vers l'instance sélectionnée dans le Target Group.
- Cible → ALB : si la réponse est invalide, incomplète, ou la connexion se ferme prématurément, l'ALB émet un 502.
- Health Check : chemin parallèle et indépendant — son succès ne garantit pas la validité des réponses applicatives réelles.
Étape 1 — Activer et Lire les Access Logs ALB
Les métriques CloudWatch vous disent qu'il y a des 502, mais les access logs ALB vous disent pourquoi. Sans eux, vous diagnostiquez à l'aveugle. Activez-les en priorité absolue si ce n'est pas déjà fait — c'est la seule source qui expose target_status_code, target_processing_time, et error_reason par requête.
# Activer les access logs sur l'ALB
aws elbv2 modify-load-balancer-attributes \
--load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/mon-alb/1234567890abcdef \
--attributes Key=access_logs.s3.enabled,Value=true \
Key=access_logs.s3.bucket,Value=mon-bucket-logs-alb \
Key=access_logs.s3.prefix,Value=alb
Une fois les logs disponibles dans S3, cherchez les champs critiques. Un 502 avec target_status_code vide ou - indique que l'ALB n'a jamais reçu de réponse valide de la cible — la connexion a été interrompue côté application avant l'envoi des headers.
# Rechercher les entrées 502 dans les logs (adapter le chemin S3)
aws s3 cp s3://mon-bucket-logs-alb/alb/ . --recursive --exclude "*" --include "*.log.gz"
zcat *.log.gz | awk '$9 == 502 {print $9, $10, $14, $15, $16}' | head -50
# Colonnes : elb_status_code, target_status_code, target_processing_time, request_processing_time, response_processing_time
Ce que vous cherchez dans la sortie :
502 -: la cible n'a jamais répondu — connexion fermée prématurément ou timeout.502 200: la cible a répondu 200 mais la réponse était malformée (headers invalides, encoding incorrect).target_processing_timeproche de 60 secondes : l'idle timeout ALB a expiré avant la fin du traitement.
Étape 2 — Vérifier l'Idle Timeout et le Keep-Alive Applicatif
Les 502 intermittents sans aucun log d'erreur côté application pointent presque toujours vers un désalignement de timeout de connexion. L'ALB maintient des connexions persistantes vers les cibles. Si votre serveur applicatif ferme la connexion keep-alive avant que l'ALB ne le fasse, l'ALB peut tenter de réutiliser une connexion déjà fermée — et obtenir un 502.
C'est comme raccrocher le téléphone une seconde avant que l'autre personne finisse de parler. Du côté de l'ALB, la ligne est coupée sans raison apparente.
# Vérifier l'idle timeout actuel de l'ALB
aws elbv2 describe-load-balancer-attributes \
--load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/mon-alb/1234567890abcdef \
--query 'Attributes[?Key==`idle_timeout.timeout_seconds`]'
La règle opérationnelle : le timeout keep-alive de votre serveur applicatif doit être supérieur à l'idle timeout de l'ALB. Si l'ALB est configuré à 60 secondes (valeur par défaut), configurez nginx à keepalive_timeout 65; ou Apache à KeepAliveTimeout 65. Cela laisse à l'ALB le temps de fermer la connexion en premier.
# Modifier l'idle timeout ALB si nécessaire
aws elbv2 modify-load-balancer-attributes \
--load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/mon-alb/1234567890abcdef \
--attributes Key=idle_timeout.timeout_seconds,Value=60
ALB 502 Bad Gateway — Arbre de Décision Diagnostic
Instances Healthy"] --> B["Activer Access Logs ALB
Lire target_status_code"] B --> C{"target_status_code
vide ou tiret ?"} C -->|Oui| D{"target_processing_time
proche idle timeout ?"} C -->|Non — code présent| E["Réponse HTTP malformée
Tester curl direct vers cible"] D -->|Oui| F["Timeout applicatif
Augmenter idle timeout ALB
ou accélérer traitement"] D -->|Non| G{"502 pendant
déploiement ?"} G -->|Oui| H["Deregistration delay trop court
Augmenter deregistration_delay"] G -->|Non| I["Vérifier protocole Target Group
HTTP vs HTTPS
Vérifier keep-alive timeout"] E --> J["Headers malformés / encoding Correction côté application"] F --> K["Aligner keep-alive serveur
supérieur à idle timeout ALB"]
- Commencer par les access logs :
target_status_codevide ou-oriente vers TCP/timeout, une valeur présente oriente vers la couche HTTP. - Si
target_processing_time≈ idle timeout → problème de timeout applicatif. - Si
target_status_codeest présent mais ALB retourne 502 → réponse HTTP malformée. - Vérifier le protocole configuré dans le Target Group (HTTP vs HTTPS) en dernier — c'est souvent négligé après une migration TLS.
Étape 3 — Valider la Réponse HTTP de l'Application Directement
Contourner l'ALB et tester la cible directement élimine toute ambiguïté sur l'origine du problème. Si la requête directe vers l'instance échoue ou produit une réponse malformée, le problème est applicatif — l'ALB n'est que le messager.
# Depuis un bastion ou une instance dans le même VPC
# Remplacer 10.0.1.50 par l'IP privée de l'instance cible et 8080 par le port applicatif
curl -v --max-time 30 http://10.0.1.50:8080/votre-endpoint
# Pour inspecter précisément les headers de réponse
curl -sI --max-time 30 http://10.0.1.50:8080/votre-endpoint
Cherchez dans la sortie curl -v : des headers HTTP malformés (espaces inattendus, caractères non-ASCII, valeurs manquantes), une connexion fermée avant la fin des headers, ou une réponse chunked encoding incorrecte. L'ALB est strict sur la conformité HTTP/1.1 — une réponse que votre navigateur accepterait peut être rejetée par l'ALB.
Étape 4 — Vérifier le Protocole et le Port du Target Group
Après une migration vers HTTPS ou un changement de configuration, il arrive que le Target Group soit configuré en HTTPS alors que l'application écoute en HTTP (ou l'inverse). L'ALB tente alors une négociation TLS vers une application qui parle HTTP en clair — la connexion échoue immédiatement et produit un 502 avec target_status_code: -.
# Vérifier le protocole et le port configurés dans le Target Group
aws elbv2 describe-target-groups \
--target-group-arns arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/mon-tg/abcdef1234567890 \
--query 'TargetGroups[*].{Protocol:Protocol,Port:Port,HealthCheckProtocol:HealthCheckProtocol,HealthCheckPort:HealthCheckPort}'
# Vérifier l'état de santé détaillé des cibles
aws elbv2 describe-target-health \
--target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/mon-tg/abcdef1234567890
Si le protocole du Target Group est incorrect, modifiez-le — mais notez qu'un Target Group ne peut pas être modifié en place pour changer de protocole. Vous devrez créer un nouveau Target Group avec le bon protocole et mettre à jour la règle du listener.
Étape 5 — Analyser les Métriques CloudWatch pour Corréler le Timing
Les access logs donnent le détail par requête, mais les métriques CloudWatch permettent de corréler les 502 avec des événements système : déploiements, pics de charge, redémarrages d'instances. Cette corrélation temporelle est souvent ce qui révèle la cause racine quand les logs individuels ne montrent pas de pattern évident.
# Récupérer le nombre de 502 sur les 3 dernières heures (résolution 1 minute)
aws cloudwatch get-metric-statistics \
--namespace AWS/ApplicationELB \
--metric-name HTTPCode_ELB_502_Count \
--dimensions Name=LoadBalancer,Value=app/mon-alb/1234567890abcdef \
--start-time $(date -u -d '3 hours ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 60 \
--statistics Sum
# Métriques de temps de traitement cible — identifier les timeouts
aws cloudwatch get-metric-statistics \
--namespace AWS/ApplicationELB \
--metric-name TargetResponseTime \
--dimensions Name=LoadBalancer,Value=app/mon-alb/1234567890abcdef \
--start-time $(date -u -d '3 hours ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 60 \
--statistics p99 Average
L'Erreur Classique : Déregistrement d'Instance Sous Charge
Voici un pattern que j'ai vu plusieurs fois en production lors de déploiements rolling : les 502 apparaissent pendant exactement la durée du déregistrement d'une instance, puis disparaissent. Le réflexe initial est de chercher un bug applicatif — les logs montrent des 502 avec target_status_code: -, les instances semblent saines, et rien dans l'application ne semble anormal.
La vraie cause : le deregistration delay (connection draining) est configuré à 0 ou à une valeur trop basse. Quand une instance est déregistrée du Target Group, l'ALB lui envoie encore des requêtes pendant la période de draining. Si cette période est trop courte, l'instance ferme ses connexions actives avant que l'ALB ne redirige le trafic — résultat : 502 en rafale pendant le déploiement.
# Vérifier le deregistration delay actuel
aws elbv2 describe-target-group-attributes \
--target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/mon-tg/abcdef1234567890 \
--query 'Attributes[?Key==`deregistration_delay.timeout_seconds`]'
# Ajuster le deregistration delay (valeur en secondes, entre 0 et 3600)
aws elbv2 modify-target-group-attributes \
--target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/mon-tg/abcdef1234567890 \
--attributes Key=deregistration_delay.timeout_seconds,Value=30
Un 502 pendant un déploiement n'est pas un bug applicatif — c'est un problème de synchronisation entre le cycle de vie de l'instance et le comportement de l'ALB.
IAM — Permissions Requises pour le Diagnostic
Si vous exécutez ces commandes depuis un rôle IAM restreint, voici les permissions minimales nécessaires pour les opérations de lecture et de modification couvertes dans ce post.
🔽 Cliquer pour afficher la politique IAM minimale
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ALBDiagnosticRead",
"Effect": "Allow",
"Action": [
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DescribeLoadBalancerAttributes",
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:DescribeTargetGroupAttributes",
"elasticloadbalancing:DescribeTargetHealth",
"cloudwatch:GetMetricStatistics",
"s3:GetObject",
"s3:ListBucket"
],
"Resource": "*"
},
{
"Sid": "ALBDiagnosticWrite",
"Effect": "Allow",
"Action": [
"elasticloadbalancing:ModifyLoadBalancerAttributes",
"elasticloadbalancing:ModifyTargetGroupAttributes"
],
"Resource": [
"arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/mon-alb/*",
"arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/mon-tg/*"
]
}
]
}
Notez que les actions Describe* sur les ressources ELB et les métriques CloudWatch nécessitent "Resource": "*" — ces actions ne supportent pas la restriction par ARN de ressource dans la politique IAM.
Conclusion et Prochaines Étapes — ALB 502 Bad Gateway
Un ALB 502 avec des instances Healthy est presque toujours un problème de couche applicative ou de timing de connexion, pas un problème d'infrastructure. L'ordre de diagnostic qui fonctionne en production : activer les access logs en premier, lire target_status_code et target_processing_time, puis tester la cible directement en contournant l'ALB.
- Consultez la documentation officielle de troubleshooting ALB pour la liste complète des codes d'erreur.
- Consultez la référence des access logs ALB pour le format complet des champs de log.
- Si les 502 persistent après ce diagnostic, activez AWS X-Ray sur votre application pour tracer les requêtes individuelles et identifier les goulots d'étranglement internes.
Glossaire
| Terme | Définition |
|---|---|
| Target Group | Groupe de cibles (instances, IPs, Lambda) vers lesquelles l'ALB route le trafic selon des règles de listener. |
| Idle Timeout | Durée maximale d'inactivité d'une connexion TCP avant que l'ALB ne la ferme. Par défaut 60 secondes sur l'ALB. |
| Keep-Alive | Mécanisme HTTP permettant de réutiliser une connexion TCP pour plusieurs requêtes, réduisant la latence de connexion. |
| Deregistration Delay | Période pendant laquelle l'ALB continue d'envoyer des requêtes à une instance en cours de déregistrement, pour permettre aux connexions actives de se terminer. |
| target_status_code | Champ des access logs ALB indiquant le code HTTP retourné par la cible. Un tiret (-) indique qu'aucune réponse n'a été reçue. |
Commentaires
Enregistrer un commentaire