Hardening load balancers: the basics
Why the front door has several settings that decide how safely and evenly it behaves
A load balancer is the front door of an internet-facing service: it terminates connections, parses HTTP, and forwards each request to a target. The way it parses, drains, and distributes that traffic is governed by a handful of attributes, and the defaults, especially on older load balancers, lean towards permissiveness and convenience rather than safety and efficiency. An Application Load Balancer (ALB) created before AWS tightened the defaults may forward malformed headers or sit in a loose HTTP parsing mode; a Classic Load Balancer (CLB) may cut live connections on every deploy or spread traffic unevenly across zones; an Auto Scaling group behind a load balancer may ignore the load balancer's own health signal entirely.
AWS Security Hub turns each weak default into its own control, which is why a single perimeter can fail several load-balancer checks at once. ELB.4 fails an ALB that forwards invalid HTTP headers; ELB.12 fails an ALB whose desync mitigation mode is monitor rather than defensive or strictest; ELB.14 is the same desync check for Classic Load Balancers; ELB.7 fails a CLB without connection draining; ELB.9 fails a CLB without cross-zone load balancing; AutoScaling.1 fails an Auto Scaling group behind a load balancer that still uses EC2-only health checks. They look like separate problems on the report, but they are one capability: make the front door reject ambiguous traffic, hand off cleanly, balance evenly, and act on the health signal it already has.
The reason these are worth closing as a group is the cost-to-fix asymmetry. Almost every one is a single attribute change with no infrastructure cost, no downtime and no redeploy, while the downsides range from request smuggling (a breach-class attack) to dropped customer requests, lopsided over-provisioning and broken instances that never get replaced. The job is to inventory every load balancer and Auto Scaling group, flip the safe values, then make those the defaults so new load balancers are born compliant.
In this lesson you will learn how a load balancer's attributes govern HTTP parsing, connection handling, traffic distribution and health checking, how to find every weakly-configured load balancer and Auto Scaling group in an account, and how to harden them with single attribute changes that take effect with no downtime. The Controls this lesson covers section lists every Security Hub control in this capability, each linking to a deep page with the exact check and a copy-and-paste fix.
Seventy thousand dollars for one malformed header
In 2019 James Kettle of PortSwigger published the HTTP desync research that put request smuggling on every security team's radar. Within months bug-bounty programmes at major vendors were paying out five-figure rewards, Kettle himself collected 70,000 dollars from a single programme for a smuggling chain that hijacked admin sessions. The attack needs nothing more than a front-end load balancer and a back-end server disagreeing about which header decides where a request ends. AWS shipped the drop_invalid_header_fields attribute and the desync mitigation modes shortly after, and made the safe values the default for new load balancers. Older ones, or ones explicitly set loose, are exactly what ELB.4, ELB.12 and ELB.14 hunt for.
Finding weakly-configured load balancers across an estate
Priya is auditing the perimeter of a fintech account ahead of a SOC 2 renewal. Security Hub has flagged several ALBs and CLBs across the production account, all public-facing, all in front of authenticated traffic.
Rather than work the findings one by one, she sweeps every ALB in the region and reads the two HTTP-hardening attributes together, so she can fix the whole cohort in one change window.
Sweep every Application Load Balancer and read its desync mode and invalid-header setting together. monitor mode fails ELB.12; a false invalid-header flag fails ELB.4.
One pass over the fleet shows which ALBs fail ELB.12 and ELB.4 together, exactly the cohort Security Hub is reporting.
How load-balancer hardening actually worksdeep dive
Most of these controls read a single attribute. On an ALB, routing.http.drop_invalid_header_fields.enabled must be true (ELB.4) and routing.http.desync_mitigation_mode must be defensive or strictest, not monitor (ELB.12). The desync mode is the broader defence: monitor logs ambiguous requests but forwards them, defensive (the AWS default) routes conformant requests and blocks the clearly-malicious ones, strictest rejects anything that is not strictly RFC 7230 compliant. On a Classic Load Balancer the equivalent desync attribute is elb.http.desyncmitigationmode (ELB.14), plus connection draining (ELB.7) and cross-zone load balancing (ELB.9). AutoScaling.1 reads a group's HealthCheckType, which must be ELB rather than EC2 when the group sits behind a load balancer.
The attribute changes are instant and non-disruptive. Flipping the invalid-header or desync attribute on a live ALB does not restart the listener, drain connections or reset in-flight requests; the only side effect is that genuinely malformed requests start being rejected, and in practice the legitimate-use fallout has been essentially zero. Connection draining and cross-zone balancing on a CLB propagate with no downtime, and cross-zone balancing carries no per-request charge on a Classic Load Balancer. AutoScaling.1 is the one with an operational caveat: switching to ELB health checks delegates termination to the load balancer's probe, so confirm that probe reflects real application health and set a HealthCheckGracePeriod above the instance cold-start time, or every fresh instance is killed before it finishes booting.
Security Hub evaluates these change-triggered through AWS Config (alb-http-drop-invalid-header-enabled, alb-desync-mode-check, autoscaling-group-elb-healthcheck-required and siblings), so a fix flips the finding to PASSED on the next evaluation. Note one relationship: AWS recommends disabling ELB.4 once ELB.12 is enabled, because the desync modes supersede plain invalid-header dropping. The durable answer is to encode the safe attribute values in your Terraform or CDK load-balancer module so new load balancers are born compliant, backed by the Config rules to catch any drift.
What is the impact of leaving load balancers unhardened?
The headline impact is HTTP request smuggling. An ALB that forwards invalid headers or sits in monitor mode, or a CLB on the loose desync setting, can let an attacker who finds a parsing disagreement between the load balancer and the backend prepend a hidden request to the next legitimate connection. In published exploits this has been used to steal session cookies, escalate to admin endpoints, and poison shared caches so every subsequent user is served attacker content, all without exploiting a single line of application code. That is a board-level event: customer data exposure, regulatory notification and reputational damage.
The reliability impacts are quieter but real. A Classic Load Balancer without connection draining cuts live requests on every deploy and scale-in, so routine operational events drop a fraction of customer traffic and cluster support tickets around deployment windows. Without cross-zone balancing, instances in one zone run hot while another zone coasts, and teams over-provision the whole fleet to compensate, paying for capacity they already had spare. An Auto Scaling group on EC2-only health checks keeps a broken-but-powered-on instance in the desired-capacity count forever: you pay for a zombie that serves no traffic, and your effective capacity quietly drops below what you provisioned, exactly when a traffic peak makes it an incident.
On the compliance side, ELB.4, ELB.12 and ELB.14 map to NIST 800-53 and PCI DSS v4.0.1 requirements for protecting public-facing web applications, so a load balancer on a permissive setting is documented evidence the perimeter accepts non-RFC traffic. The remediation, by contrast, is mostly a single attribute change. Few controls have a cleaner cost-benefit profile, near-zero effort against breach-class, revenue and availability downsides.
How do you harden load balancers safely?
Work the capability as one loop rather than chasing individual findings. Most steps are a single attribute flip; the two with operational caveats (the desync modes and ELB health checks) just need a quick check before you change them.
1. Inventory every load balancer and Auto Scaling group
Across every region and account, list Application Load Balancers (read drop_invalid_header_fields and desync_mitigation_mode), Classic Load Balancers (read the desync mode, connection draining and cross-zone balancing) and Auto Scaling groups behind a load balancer (read HealthCheckType and HealthCheckGracePeriod). Do not trust the Security Hub count alone, run the describe sweep yourself, because older load balancers and ones created from older IaC modules default to the unsafe values. Record the ARN, account, region and the service each fronts.
2. Confirm the few changes that have a caveat
Most flips are safe to apply blind. Two are not. For the desync modes, check the ALB access-log classification field over recent traffic: only Compliant and Acceptable classifications mean defensive is safe, while Ambiguous or Severe entries mean some (usually legacy) client sends non-conformant requests, investigate before blocking. For AutoScaling.1, confirm the target group's health probe reflects real application health and set a grace period above the cold-start time before switching to ELB checks, or fresh instances are killed in a loop.
3. Flip the safe values, highest impact first
Set drop_invalid_header_fields to true and desync_mitigation_mode to defensive on ALBs, the desync mode to defensive on CLBs, enable connection draining and cross-zone balancing on CLBs, and switch load-balanced Auto Scaling groups to ELB health checks with a sensible grace period. Prioritise the HTTP-hardening controls on public, authenticated services first, since those carry the breach risk. Each change is a single API call per resource and takes effect immediately with no downtime. After enabling cross-zone balancing or ELB health checks, right-size the fleet so you capture the saving rather than leaving the headroom running.
4. Prevent recurrence with AWS Config and IaC defaults
Cleanup without prevention just resets the clock. Enable the AWS Config managed rules (alb-http-drop-invalid-header-enabled, alb-desync-mode-check, autoscaling-group-elb-healthcheck-required and the CLB equivalents) so any new non-compliant resource raises an event within minutes, and set the safe attribute values as defaults in your Terraform or CDK modules with a CI check that refuses a definition leaving them unset. New load balancers are then compliant from creation.
# 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 Quick quiz
Question 1 of 5Security Hub shows ELB.4, ELB.12, ELB.7, ELB.9 and AutoScaling.1 findings across the perimeter. What is the most efficient way to think about them?
You scored
0 / 5
Keep learning
Go deeper on the load-balancer controls in this capability, the attacks they prevent, and how to enforce the safe values.
- Security Hub controls for Elastic Load Balancing The authoritative definitions for ELB.4, ELB.7, ELB.9, ELB.12 and ELB.14, with severities and remediation guidance.
- Application Load Balancer attributes reference Full list of ALB attributes including drop_invalid_header_fields and desync_mitigation_mode and how each mode handles request classifications.
- Add Elastic Load Balancing health checks to an Auto Scaling group Step-by-step guide to enabling ELB health checks for AutoScaling.1, including the role of the health check grace period.
You can now treat load-balancer hardening as one capability rather than a scatter of findings: inventory every load balancer and Auto Scaling group, confirm the two changes that carry a caveat, flip the safe attribute values highest-impact first, and prevent recurrence with Config rules and IaC defaults. The Controls this lesson covers section below links every control in this group to its deep page and fix.
Back to the library