Skip to main content
emnode
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).

Is ELB.9 a false positive?

A CLB that is intentionally pinned to a single Availability Zone (a zonal workload kept in one AZ for data-locality or latency reasons, or one constrained to a single zone by capacity reservations) gains nothing from cross-zone balancing, since there is only one zone to spread across, yet it still fails ELB.9 because the control just reads the CrossZoneLoadBalancing attribute. Some operators also keep it off deliberately to avoid cross-AZ data transfer charges on high-volume east-west traffic. Where the single-zone or cost-driven choice is deliberate and documented, suppress the finding in Security Hub with the rationale rather than enabling cross-zone purely to satisfy the check; reserve the enabled requirement for the multi-AZ load balancers where uneven distribution is a real risk.

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