Add WAF to an ALB
Add WAF to an ALB in minutes with this hands-on Cloud security essentials tutorial. Understand the core concept, follow step-by-step instructions, and troubleshoot common issues.
Focus: add waf to an alb in minutes
You’ve finally got your app behind an Application Load Balancer (ALB) — traffic is flowing, health checks are green, and life is good. Then the alarms start: a spike in requests to /wp-login.php, a botnet brute-forcing your login endpoint, or a weird SQL injection payload slipping past your app-level filters. You know you need a Web Application Firewall (WAF) in front of that balanced traffic, but the thought of a multi-day infrastructure project makes your stomach drop. The good news: adding AWS WAF to an ALB is a 10-minute task — if you understand the pieces and the one command that ties them together. This lesson walks you through exactly that.
The problem this lesson solves
Modern web traffic isn’t just users clicking around. It’s credential-stuffing bots, SQL injection probes, cross-site scripting (XSS) attempts, and Layer 7 DDoS floods that can bring your API to its knees. Your ALB is the front door to your application, and with no WAF, that door is wide open — every request reaches your backend untouched.
You could build security into your application code, but that’s reactive, slow to update, and easy to get wrong. AWS WAF gives you a managed, policy-based layer that inspects HTTP(S) traffic before it hits your ALB. And the best part? You don’t need a dedicated security team or a week of migration. You can attach a WAF web ACL to an existing ALB in minutes using the AWS console, CLI, or infrastructure-as-code.
Without this integration, you may already know the symptoms are real, but here’s the “why now”: attackers share exploit kits faster than any dev team can patch. A WAF gives you the speed-to-coverage that your code can’t. It’s not a replacement for secure coding — it’s the shield on the other side of the moat.
Core concept / mental model
Think of your ALB as a toll booth on a highway. Every car that arrives is inspected (L7 routing), but without a WAF, you don’t check the cargo — you just wave it through. AWS WAF is the inspection station you place just before the toll booth. It checks every vehicle (request) for contraband (malicious patterns), lets the clean ones pass, and turns the bad ones away at the gate.
In AWS terms:
- Web ACL — the rule-group container. This is your inspection station’s rulebook.
- Rules — each rule is a test, like “block if user-agent contains
python-requests” or “block if the request matches the SQL injection managed rule.” - Rule groups — bundles of rules you can reuse across multiple ACLs (for example, the AWS-managed core rule set).
- Association — the step where you attach the ACL to your ALB. This is the “attach the station to the booth” moment.
The power is in the order of evaluation. AWS WAF runs your rules in a defined priority, and the first rule that matches with a Block action stops the request. If no rule matches, the request flows on to the ALB normally. This means you can start with block-only rules, and later add count-only rules to monitor traffic without breaking anything.
How it works step by step
Attaching a WAF to an ALB is conceptually simple, but the step order matters. Here’s the logical flow:
- Create or identify a web ACL — You need an ACL with at least one rule group attached. For a first pass, start with the AWS Managed Rules core rule set (CRS).
- Add rules to the ACL — Choose managed rule groups or write your own custom rules. Set actions (
Allow,Block,Count) and priorities. - Associate the ACL to your ALB — The association tells AWS which resources (ALBs, API Gateways, CloudFront) the ACL protects.
- Test and monitor — Verify the ACL is actually influencing traffic and tune rules based on WAF logs and metrics.
- Iterate — Lower rule tolerances, add IP reputation lists, and review blocked requests regularly.
The key to “in minutes” is not hand-crafting everything. Use AWS-managed rules to get instant protection, then customize for your app’s quirks.
Why association order matters
You cannot attach a web ACL to an ALB that already has over 5 ACLs (there’s a hard limit). Also, the ACL must be in the same AWS region as the ALB. If you’re using eu-west-1 for your ALB, your WAF ACL has to live there too — don’t create it in us-east-1 and expect to attach it.
Hands-on walkthrough
Let’s get your hands dirty. We’ll cover the console (visual, good for a one-off), then the AWS CLI (scriptable, repeatable), and finally an Infrastructure-as-Code snippet for the truly automated crowd.
Prerequisites
- An existing Application Load Balancer (ALB) in your AWS account. You should know its ARN.
- AWS CLI installed and configured with credentials that have
wafv2:andelasticloadbalancing:permissions. - (Optional) An understanding of Amazon CloudWatch — we’ll use it to peek at metrics.
Option A — Console (fastest, 5 minutes)
- Open the AWS WAF & Shield console. Click Web ACLs in the left sidebar.
- Click Create web ACL.
- Resource type: Regional
- Region: The region where your ALB lives.
- Name:
alb-waf-acl - In Associated AWS resources, click Add AWS resources and select your ALB.
- In Rules, click Add rules → Add managed rule groups.
- Search for AWS Managed Rules — choose Core rule set (CRS) and leave the action as Count for now. (We’ll switch to Block after testing.)
- Click Next, review, then Create web ACL.
That’s it! The ACL is now attached to your ALB. Traffic flows normally because the rules are in Count mode — they’re watching, not stopping.
Option B — CLI (repeatable, 2 minutes)
For a scriptable approach, get your ALB ARN and create a simple ACL with a managed rule group. Here’s a complete example:
# 1. Get your ALB ARN (replace the name)
ALB_ARN=$(aws elbv2 describe-load-balancers \
--names my-alb \
--query 'LoadBalancers[0].LoadBalancerArn' \
--output text)
# 2. Create a web ACL that Counts traffic (safe to start)
WAF_ARN=$(aws wafv2 create-web-acl \
--name alb-waf-acl \
--scope REGIONAL \
--region us-east-1 \
--default-action Allow={} \
--description "Protecting ALB from common attacks" \
--rules '[
{
"Name": "AWS-AWSManagedRulesCommonRuleSet",
"Priority": 1,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesCommonRuleSet"
}
},
"OverrideAction": {
"Count": {}
}
}
]' \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=alb-waf-metrics \
--query 'Summary.ARN' \
--output text)
echo "Web ACL ARN: $WAF_ARN"
# 3. Associate it with your ALB
aws wafv2 associate-web-acl \
--web-acl-arn "$WAF_ARN" \
--resource-arn "$ALB_ARN"
Expected output:
Web ACL ARN: arn:aws:wafv2:us-east-1:123456789012:regional/webacl/alb-waf-acl/abc123...
And no error from the associate command.
Option C — Infrastructure as Code (Terraform)
Here’s the same setup in Terraform, for teams that version-control everything:
resource "aws_wafv2_web_acl" "alb_acls" {
name = "alb-waf-acl"
scope = "REGIONAL"
description = "Protecting ALB from common attacks."
default_action {
allow {}
}
rule {
name = "AWS-AWSManagedRulesCommonRuleSet"
priority = 1
override_action {
count {}
}
statement {
managed_rule_group_statement {
vendor_name = "AWS"
name = "AWSManagedRulesCommonRuleSet"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "alb-waf-common"
sampled_requests_enabled = true
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "alb-waf-metric"
sampled_requests_enabled = true
}
}
resource "aws_wafv2_web_acl_association" "alb" {
resource_arn = aws_lb.my_alb.arn
web_acl_arn = aws_wafv2_web_acl.alb_acls.arn
}
After terraform apply, your ALB is protected and the config is code-reviewed.
Verification
Now confirm the ACL is active and capturing data:
# List all ACLs and check the association
aws wafv2 list-web-acls --scope REGIONAL --region us-east-1
# Get CloudWatch metrics for the ACL (replace with your metric name)
aws cloudwatch get-metric-statistics \
--namespace AWS/WAFV2 \
--metric-name CountedRequests \
--dimensions Name=WebACL,Value=alb-waf-acl Name=Rule,Value=ALL \
--start-time $(date -u -d '5 minutes ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 300 \
--statistics Sum
If you see CountedRequests climbing, your WAF is awake — even in Count mode.
Compare options / when to choose what
There are several ways to add WAF protection to your ALB. Here’s a comparison to help you pick the best path for your situation:
| Approach | Best for | Speed | Maintainability | Notes |
|---|---|---|---|---|
| AWS Managed Rules (core set) | Fast, out-of-the-box protection | Minutes | Low — AWS updates rules | Start with Count mode to avoid blocking legit traffic |
| Custom rules (rate-based, IP blocklists) | Specific app quirks, geo-blocking | Same day | Medium — you maintain logic | Combine with managed rules for layered depth |
| Third-party WAF (e.g., Cloudflare, F5) | Advanced bot detection, DDoS mitigation | Hours | Can be high — vendor integration | Better if you need very granular bot scoring |
| API Gatway WAF (alternative) | If you’re using API Gateway, not ALB | Minutes | Similar to ALB | Choice depends on your entry point, not WAF features |
Decision guide:
- Don’t over-engineer. Start with the AWS Managed CRS in Count mode — it covers OWASP Top 10.
- If you have a specific attack pattern (e.g., a nasty bot hitting /checkout), add a rate-based rule.
- If your threat model demands AI-driven bot detection or massive DDoS scrubbing, consider a third-party service, but expect more setup.
- If you’re building everything on API Gateway, don’t force an ALB WAF — use WAF directly on the gateway (that’s a different lesson, though).
For the 90% case, the AWS managed + custom rate rule combination is bulletproof and cheap.
Troubleshooting & edge cases
Even a “simple” attach can hit snags. Here are the most common problems and their fixes.
Error: WAFUnableToAssociateException
Symptom: The associate command fails with a message like Web ACL cannot be associated with the resource.
Cause: The ACL and ALB are in different regions, or the ALB already has 5 ACLs attached (hard limit).
Fix: Double-check the --region flag on both the ACL and ALB. If you hit the limit, remove an existing association first. This error rarely means permission issues — but if you’re using a restricted role, verify elasticloadbalancing:DescribeLoadBalancers and wafv2:AssociateWebACL are allowed.
Traffic suddenly blocked (when you switched to Block)
Symptom: After changing the action from Count to Block, legit users start seeing 403s.
Cause: Your rules are too aggressive — for example, rate-based rules with too low a threshold, or the CRS blocking based on a false positive (like a narrow user-agent).
Fix: Use Sampled requests and CloudWatch metrics to see which rule is firing. Ramp the threshold up, or whitelist specific users via an IP set, then re-test in Count mode first. Always keep the ACL in Count for at least 24 hours before going full-block.
Sampled requests are empty
Symptom: The WAF console shows no sampled requests, even though your ALB is handling traffic.
Cause: Visibility config is disabled, or the sampled requests are not enabled on the rule group.
Fix: Ensure SampledRequestsEnabled=true in the visibility config (both on the ACL and the rule). This is in the CLI example above — don’t skip it. Then check CloudWatch metrics to confirm data is coming in.
AWS Managed Rule blocks my API’s POST with a JSON body
Symptom: Your API starts receiving 403s on Content-Type: application/json posts.
Cause: The CRS includes rules like CrossSiteScripting_BODY that can false-positive if your JSON contains HTML-like strings.
Fix: Use exclude rules in the rule group — for example, exclude the specific rule ID, or switch the rule action to Count for that path. Always test with real payloads in a staging environment.
What you learned & what's next
You now know how to add a WAF to an ALB in minutes, not days. You’ve seen:
- The exact mental model (inspection station before the toll booth) that demystifies web ACLs, rule groups, and associations.
- The step-by-step flow from creating a web ACL to associating it with an ALB, with three implementation paths: console, CLI, and Terraform.
- How to compare managed rules vs. custom rules vs. third-party options and pick the right one for your threat model.
- The top troubleshooting gotchas — region mismatches, aggregation limits, and the critical “Count first, then Block” discipline.
This knowledge directly meets the learning objectives: you can explain what a WAF-to-ALB integration does, and you’ve completed a hands-on exercise that attaches one. You also know how to monitor and tune it — a skill that separates rookies from pros.
Next in the track: Once your WAF is live, you should care about who can access your ALB from a networking layer. The next lesson moves from Layer 7 to Layer 4 — covering security groups and network ACLs to tighten your VPC perimeter. That’s where you’ll learn to restrict traffic at the network level and combine that with WAF for defence in depth.
Practice recap
Mini-exercise: In your AWS account, create a new web ACL with the AWS Managed Rules core rule set in Count mode, associate it with a test ALB (or a real one if you’re careful), and then generate some traffic — check the sampled requests in the WAF console. Next, try creating a rate-based rule that blocks more than 100 requests per IP per 5 minutes and test it with a small script. Once you see the metrics, reverse the rate rule to Count and clean up the ACL to avoid charges.
Common mistakes
- Creating the web ACL in the wrong region — WAFv2 resources are regional, and the ACL must be in the same region as the ALB or the association fails.
- Going with
Blockaction immediately on managed rules — this often breaks legitimate traffic. Always start inCountmode to see what the rules would block. - Forgetting the hard limit of 5 web ACL associations per ALB; if your ALB is already crowded, you’ll get
WAFUnableToAssociateExceptionuntil you remove an old association. - Neglecting visibility config — if
SampledRequestsEnabledis false, you’re flying blind and can’t debug why traffic is blocked or missed.
Variations
- Use AWS WAF on API Gateway instead of ALB if your front door is a REST or HTTP API — same rule groups, but you associate the ACL with the API Gateway stage, not the load balancer.
- Layer a rate-based rule on top of managed rules to throttle IPs that send too many requests per 5-minute window — useful for brute-force protection on login endpoints.
- Leverage AWS Firewall Manager to deploy the same web ACL across multiple ALBs and accounts, so you don’t have to attach each one manually.
Real-world use cases
- Attaching WAF to an ALB fronting a public e-commerce site, blocking SQL injection and XSS in product search endpoints while allowing normal browsing.
- Protecting an ALB that serves a mobile app backend API with a rate-based rule to prevent credential-stuffing attacks on the login endpoint.
- Using WAF with managed rules to filter out bad bot traffic and scrapers from a content website, reducing server load and preventing content theft.
Key takeaways
- AWS WAF attaches to your ALB via a web ACL, creating an inspection layer that filters malicious HTTP(S) requests before they reach your app.
- Always start with AWS-managed rules in
Countmode, monitor for a day, then switch toBlockto avoid false positives breaking live traffic. - The WAF and ALB must be in the same region — mismatched regions cause association errors.
- Use CloudWatch metrics and sampled requests to tune rule priorities and thresholds based on actual traffic.
- Managed rules (CRS) give instant, maintained protection; custom rules fill gaps like specific IP blocking or high-rate bot throttling.
- Scripting via CLI or Terraform makes the setup repeatable and auditable, turning a one-time fix into a sustainable security control.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.