Skip to content
AWS aws security 6 min read

AWS WAF (Web Application Firewall)

AWS WAF (Web Application Firewall) is a service that inspects every HTTP and HTTPS request heading to your application and decides whether to allow it, block it, or just count it. It looks inside the request — the URL, headers, query string, cookies, and body — and matches it against rules you choose. This lets you stop common attacks like SQL injection and cross-site scripting before they ever reach your servers. Because it runs at the edge or in front of your load balancer, it filters bad traffic without you writing any application code.

What WAF inspects and how it decides

WAF works at layer 7 (the application layer), which means it understands the actual web request, not just raw network packets. For each incoming request, WAF runs through an ordered list of rules inside a web ACL (Access Control List — a container that holds your rules and a default action). Each rule has a condition (for example, “the URI contains <script>”) and an action: Allow, Block, or Count. The first rule whose condition matches decides what happens. If no rule matches, the web ACL’s default action (usually Allow) takes over.

You attach one web ACL to a resource. WAF supports three kinds of resources:

ResourceScopeBest for
CloudFront distributionGlobal (CLOUDFRONT)Public websites and static/dynamic content served at the edge
Application Load Balancer (ALB)RegionalApps running on EC2, ECS, or EKS behind an ALB
API Gateway / AppSync / CognitoRegionalREST and GraphQL APIs, and user pool sign-in flows

Gotcha: A CloudFront web ACL must be created in the us-east-1 Region with scope CLOUDFRONT, no matter where your origin lives. Regional resources (ALB, API Gateway) use a web ACL created in their own Region with scope REGIONAL.

Types of rules you can add

  • Managed rule groups — pre-built, AWS-maintained rule sets. The free AWS Managed Rules include the Core Rule Set (broad protection), SQL database rules (SQLi — SQL injection), known bad inputs, and IP reputation lists. AWS Marketplace also sells third-party rule groups (for example from Fortinet or F5) for a fee.
  • Rate-based rules — block an IP address that sends more than N requests in a 5-minute window. Good for slowing down brute-force logins or scraping.
  • IP set and geo-match rules — allow or block specific IP ranges (CIDR blocks) or whole countries.
  • Custom rules — your own conditions on strings, regex, request size, or labels from other rules.

When to use WAF: any internet-facing web app or API where you want to filter malicious requests, enforce geographic restrictions, or throttle abusive clients. When NOT to rely on it: WAF does not stop large network or transport floods — that is the job of AWS Shield (DDoS protection). WAF defends against application-layer attacks; Shield defends against volumetric and protocol attacks. Use both together for full coverage.

Create a web ACL with a managed rule group

Console steps

  1. Open the WAF & Shield console and choose Web ACLs.
  2. Pick the correct Region (use Global (CloudFront) for CloudFront, or the resource’s Region for ALB/API Gateway). Click Create web ACL.
  3. Give it a name like prod-web-acl, then under Associated AWS resources click Add AWS resources and select your CloudFront distribution or ALB.
  4. Click Add rules → Add managed rule groups, expand AWS managed rule groups, and toggle on Core rule set and SQL database.
  5. For each managed group, set the action to Count for now (you will switch to Block after tuning — see below).
  6. Set the web ACL default action to Allow, review, and click Create web ACL.

CLI equivalent

First create the web ACL, then associate it with a resource. This example is regional (for an ALB).

aws wafv2 create-web-acl \
  --name prod-web-acl \
  --scope REGIONAL \
  --region us-east-1 \
  --default-action Allow={} \
  --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=prodWebAcl \
  --rules '[{
    "Name":"AWSManagedRulesCommonRuleSet",
    "Priority":1,
    "OverrideAction":{"Count":{}},
    "Statement":{"ManagedRuleGroupStatement":{"VendorName":"AWS","Name":"AWSManagedRulesCommonRuleSet"}},
    "VisibilityConfig":{"SampledRequestsEnabled":true,"CloudWatchMetricsEnabled":true,"MetricName":"commonRules"}
  }]'

Output:

{
    "Summary": {
        "Name": "prod-web-acl",
        "Id": "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111",
        "ARN": "arn:aws:wafv2:us-east-1:111122223333:regional/webacl/prod-web-acl/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111",
        "LockToken": "0a1b2c3d-4e5f-6789-abcd-EXAMPLE22222"
    }
}

Now attach it to an ALB using the resource’s ARN:

aws wafv2 associate-web-acl \
  --web-acl-arn arn:aws:wafv2:us-east-1:111122223333:regional/webacl/prod-web-acl/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111 \
  --resource-arn arn:aws:elasticloadbalancing:us-east-1:111122223333:loadbalancer/app/my-alb/0a1b2c3d4e5f \
  --region us-east-1

Note: OverrideAction: Count puts the whole managed group into count mode so nothing is blocked yet. Once you trust the rules, change it to OverrideAction: None so each rule’s built-in Block action applies.

Add a rate-based rule

A rate-based rule blocks any single IP that sends more than the limit in a rolling 5-minute window. The minimum limit is 100 requests.

aws wafv2 update-web-acl \
  --name prod-web-acl --scope REGIONAL --region us-east-1 \
  --id a1b2c3d4-5678-90ab-cdef-EXAMPLE11111 \
  --lock-token 0a1b2c3d-4e5f-6789-abcd-EXAMPLE22222 \
  --default-action Allow={} \
  --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=prodWebAcl \
  --rules '[{
    "Name":"RateLimit",
    "Priority":2,
    "Action":{"Block":{}},
    "Statement":{"RateBasedStatement":{"Limit":2000,"AggregateKeyType":"IP"}},
    "VisibilityConfig":{"SampledRequestsEnabled":true,"CloudWatchMetricsEnabled":true,"MetricName":"rateLimit"}
  }]'

The same update-web-acl call replaces the full rule list, so always include every rule you want to keep.

Tune before you enforce: count mode

The single biggest WAF mistake is turning on aggressive managed rules in Block mode on day one. Real users get caught by false positives — for example, a legitimate form post that happens to look like SQL injection gets blocked, and your support inbox fills up.

The safe workflow:

  1. Deploy all rules in Count mode. They record matches but never block.
  2. Enable logging to Amazon CloudWatch Logs, S3, or Kinesis Data Firehose, and watch the Count metrics for a few days under real traffic.
  3. Inspect sampled requests in the console (Web ACL → Sampled requests) to see what would have been blocked.
  4. Add rule exclusions or scope-down statements for any rule causing false positives.
  5. Only then switch the action from Count to Block.

Warning: Switching managed rules straight to Block without a tuning period is the most common cause of accidental outages with WAF. Always count first.

Cost notes

WAF billing has three parts, and they add up:

  • $5.00 per web ACL per month.
  • $1.00 per rule per month (a managed rule group counts as one rule for this charge).
  • $0.60 per million requests inspected.

AWS Managed Rules are free to use (no extra per-group fee beyond the $1 rule charge); Marketplace rule groups carry their own subscription price. As a rough example, one web ACL with 5 rules handling 50 million requests a month costs about $5 + (5 × $1) + (50 × $0.60) = $40/month. Rate-based and complex rules can add small extra inspection charges, so check the pricing page for current rates.

Best practices

  • Start every managed rule group in Count mode and tune for several days before switching to Block.
  • Always turn on WAF logging and CloudWatch metrics so you can see what is being blocked and why.
  • Add a rate-based rule on login and API endpoints to blunt brute-force and scraping attacks.
  • Pair WAF with AWS Shield for DDoS coverage — WAF alone does not stop network floods.
  • Use scope-down statements to apply expensive rules only to the paths that need them, which lowers cost.
  • Keep your web ACL in Infrastructure as Code (CloudFormation or Terraform) so rule changes are reviewed and reversible.
  • Order rules by priority carefully: put cheap allow/block lists (IP sets, geo) before expensive managed groups.
Last updated June 15, 2026
Was this helpful?