Skip to main content
emnode / learn
Compliance Medium severity

AWS Security Hub · ELB

ELB.9: CLBs should have cross-zone balancing

Written and reviewed by Emnode · Last reviewed

What does AWS Security Hub ELB.9 check?

ELB.9 fails when a Classic Load Balancer has cross-zone load balancing disabled. With it off, each CLB node distributes traffic only to targets in its own Availability Zone.

Why does ELB.9 matter?

Without cross-zone balancing, traffic is split evenly per node rather than per instance, so a zone with fewer healthy targets gets the same share as a busy one — leading to lopsided load, hot instances and over-provisioning to compensate. Enabling it spreads requests evenly across all registered targets. This is one of the rare findings where the compliance fix and a real cost saving are the same single action.

How do I fix ELB.9?

  1. Audit CLBs and check the CrossZoneLoadBalancing attribute with describe-load-balancer-attributes.
  2. Enable it with modify-load-balancer-attributes; the change propagates with no downtime.
  3. Confirm targets are now receiving balanced traffic and right-size the fleet down if it was over-provisioned.
  4. Default cross-zone balancing on in your IaC so new CLBs are compliant.

Remediation script · bash

# Harden every Application Load Balancer in the region: reject invalid headers and
# require defensive (or strictest) desync mode. Both are instant, non-disruptive flips.
for arn in $(aws elbv2 describe-load-balancers \
    --query 'LoadBalancers[?Type==`application`].LoadBalancerArn' --output text); do
  aws elbv2 modify-load-balancer-attributes --load-balancer-arn "$arn" \
    --attributes \
      Key=routing.http.drop_invalid_header_fields.enabled,Value=true \
      Key=routing.http.desync_mitigation_mode,Value=defensive
  echo "$arn: hardened"
done

# Switch load-balanced Auto Scaling groups to ELB health checks with a safe grace period
# (confirm the target-group probe reflects real app health first).
for g in $(aws autoscaling describe-auto-scaling-groups \
    --query 'AutoScalingGroups[?(LoadBalancerNames!=`[]` || TargetGroupARNs!=`[]`) && HealthCheckType==`EC2`].AutoScalingGroupName' \
    --output text); do
  aws autoscaling update-auto-scaling-group --auto-scaling-group-name "$g" \
    --health-check-type ELB --health-check-grace-period 300
  echo "$g: now using ELB health checks"
done

Full walkthrough (console steps, edge cases and verification) in the lesson Harden load balancers (ALB/NLB/CLB).

Part of the learning path Build in resilience
  • ELB.1 ALB serves HTTP without redirecting to HTTPS
  • ELB.2 CLB SSL/HTTPS listeners should use ACM certs
  • ELB.3 CLB listeners should use HTTPS/TLS termination
  • ELB.4 ALB accepts malformed HTTP headers
  • ELB.5 Load balancers are not writing access logs
  • ELB.6 Load balancers can be deleted by accident
  • ELB.7 CLBs should have connection draining
  • ELB.8 CLB SSL listeners should use strong policy
  • ELB.10 CLBs should span multiple AZs
  • ELB.12 ALB desync mitigation mode
  • ELB.13 A single-AZ load balancer is a data-plane single point of failure
  • ELB.14 CLB desync mitigation mode