Tarek Cheikh
Founder & AWS Security Expert
AWS WAF (Web Application Firewall) is your first line of defense against application-layer attacks. It inspects every HTTP/HTTPS request hitting your internet-facing resources and decides whether to allow, block, count, or challenge each one. Without WAF, your applications are directly exposed to SQL injection, cross-site scripting (XSS), credential stuffing, bot scraping, and volumetric DDoS attacks.
In 2025, Cloudflare reported that automated bot traffic accounted for over 30% of all internet traffic, with malicious bots responsible for credential stuffing, inventory hoarding, and content scraping at industrial scale. AWS WAF v2, combined with Bot Control and Fraud Control managed rule groups, provides a layered defense that adapts to these evolving threats. Getting WAF right can mean the difference between a secure application and a costly breach.
This guide covers 12 battle-tested AWS WAF best practices, each with real AWS CLI commands, audit procedures, and the latest 2025-2026 updates from AWS.
AWS WAF can protect Amazon CloudFront distributions, Application Load Balancers (ALBs), Amazon API Gateway REST APIs, AWS AppSync GraphQL APIs, Amazon Cognito user pools, AWS App Runner services, and AWS Amplify Hosting apps. Every internet-facing resource without a WAF web ACL is an unprotected entry point.
# Create a web ACL for a regional resource (ALB, API Gateway)
aws wafv2 create-web-acl --name "production-web-acl" --scope REGIONAL --default-action Allow={} --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=ProductionWebACL --region eu-west-1
# Associate the web ACL with an ALB
aws wafv2 associate-web-acl --web-acl-arn arn:aws:wafv2:eu-west-1:123456789012:regional/webacl/production-web-acl/EXAMPLE-ID --resource-arn arn:aws:elasticloadbalancing:eu-west-1:123456789012:loadbalancer/app/my-alb/EXAMPLE
# List all resources associated with a web ACL
aws wafv2 list-resources-for-web-acl --web-acl-arn arn:aws:wafv2:eu-west-1:123456789012:regional/webacl/production-web-acl/EXAMPLE-ID --resource-type APPLICATION_LOAD_BALANCER
Security Hub Control: [WAF.10] AWS WAF web ACLs should have at least one rule or rule group. An empty web ACL with no rules provides a false sense of security.
AWS Managed Rules are pre-built rule groups maintained by the AWS Threat Research Team. They provide immediate protection against OWASP Top 10 vulnerabilities without requiring you to write custom rules.
# Add the Core Rule Set to an existing web ACL
aws wafv2 update-web-acl --name "production-web-acl" --scope REGIONAL --id EXAMPLE-WEB-ACL-ID --lock-token EXAMPLE-LOCK-TOKEN --default-action Allow={} --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=ProductionWebACL --rules '[
{
"Name": "AWS-AWSManagedRulesCommonRuleSet",
"Priority": 1,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesCommonRuleSet"
}
},
"OverrideAction": {"None": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "AWSManagedRulesCommonRuleSet"
}
},
{
"Name": "AWS-AWSManagedRulesKnownBadInputsRuleSet",
"Priority": 2,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesKnownBadInputsRuleSet"
}
},
"OverrideAction": {"None": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "AWSManagedRulesKnownBadInputsRuleSet"
}
}
]' --region eu-west-1
Best practice: Deploy managed rules in Count mode first. Monitor the CloudWatch metrics and sampled requests for 24-72 hours to identify false positives before switching to Block mode. Subscribe to the SNS topic for each managed rule group to receive version update notifications.
AWS WAF Bot Control identifies and manages bot traffic at two levels: Common (included with WAF) and Targeted (advanced, additional cost). Bots account for a significant share of internet traffic, and without Bot Control, your applications serve scrapers, credential stuffers, and inventory hoarders as if they were legitimate users.
# Add Bot Control (targeted level) with scope-down statement
# Only inspect requests to sensitive endpoints to optimize cost
aws wafv2 update-web-acl --name "production-web-acl" --scope REGIONAL --id EXAMPLE-WEB-ACL-ID --lock-token EXAMPLE-LOCK-TOKEN --default-action Allow={} --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=ProductionWebACL --rules '[
{
"Name": "AWS-AWSManagedRulesBotControlRuleSet",
"Priority": 3,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesBotControlRuleSet",
"ManagedRuleGroupConfigs": [
{
"AWSManagedRulesBotControlRuleSet": {
"InspectionLevel": "TARGETED"
}
}
],
"ScopeDownStatement": {
"ByteMatchStatement": {
"SearchString": "/api/",
"FieldToMatch": {"UriPath": {}},
"TextTransformations": [{"Priority": 0, "Type": "NONE"}],
"PositionalConstraint": "STARTS_WITH"
}
}
}
},
"OverrideAction": {"None": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "AWSManagedRulesBotControlRuleSet"
}
}
]' --region eu-west-1
Cost optimization: Use scope-down statements to limit Bot Control evaluation to sensitive paths (login, checkout, APIs). You are only charged for requests inspected by the rule group, not those excluded by the scope-down statement. AWS recommends running Bot Control before ATP and ACFP to keep costs down.
Rate-based rules automatically block IP addresses that exceed a defined request threshold within a 5-minute window. They are your primary defense against HTTP flood DDoS attacks and brute force login attempts.
/login or /auth paths.# Create a rate-based rule that blocks IPs exceeding 2000 requests in 5 minutes
aws wafv2 update-web-acl --name "production-web-acl" --scope REGIONAL --id EXAMPLE-WEB-ACL-ID --lock-token EXAMPLE-LOCK-TOKEN --default-action Allow={} --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=ProductionWebACL --rules '[
{
"Name": "RateLimitGlobal",
"Priority": 0,
"Statement": {
"RateBasedStatement": {
"Limit": 2000,
"AggregateKeyType": "IP"
}
},
"Action": {"Block": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "RateLimitGlobal"
}
},
{
"Name": "RateLimitLogin",
"Priority": 1,
"Statement": {
"RateBasedStatement": {
"Limit": 100,
"AggregateKeyType": "IP",
"ScopeDownStatement": {
"ByteMatchStatement": {
"SearchString": "/login",
"FieldToMatch": {"UriPath": {}},
"TextTransformations": [{"Priority": 0, "Type": "LOWERCASE"}],
"PositionalConstraint": "STARTS_WITH"
}
}
}
},
"Action": {"Block": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "RateLimitLogin"
}
}
]' --region eu-west-1
2025 Update: AWS WAF now supports additional aggregation keys beyond IP, including forwarded IP, custom header, query argument, cookie, and label namespace. You can combine up to five aggregation keys for fine-grained rate limiting (e.g., rate-limit per IP per URI path).
AWS WAF Fraud Control provides two specialized managed rule groups that go far beyond traditional WAF rules:
{
"Name": "AWS-AWSManagedRulesATPRuleSet",
"Priority": 5,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesATPRuleSet",
"ManagedRuleGroupConfigs": [
{
"AWSManagedRulesATPRuleSet": {
"LoginPath": "/api/auth/login",
"RequestInspection": {
"PayloadType": "JSON",
"UsernameField": {"Identifier": "/username"},
"PasswordField": {"Identifier": "/password"}
},
"ResponseInspection": {
"StatusCode": {
"SuccessCodes": [200],
"FailureCodes": [401, 403]
}
}
}
}
]
}
},
"OverrideAction": {"None": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "AWSManagedRulesATPRuleSet"
}
}
Best practice: Integrate the AWS WAF JavaScript SDK into your login and registration pages. The SDK generates a token that ATP and ACFP use to correlate requests with browser sessions, dramatically improving detection accuracy. Without the SDK, these rule groups rely solely on request-level inspection and miss session-level attack patterns.
Cost optimization: Use scope-down statements to limit ATP evaluation to your login path and ACFP to your registration path. Run Bot Control before ATP/ACFP in your rule priority order, as Bot Control filters out obvious bots at a lower per-request cost.
AWS WAF CAPTCHA and Challenge actions verify that requests come from real browsers operated by humans, not from scripts or automated tools.
# Create a rule that applies CAPTCHA to login page
# Then the token protects subsequent API calls
aws wafv2 update-web-acl --name "production-web-acl" --scope REGIONAL --id EXAMPLE-WEB-ACL-ID --lock-token EXAMPLE-LOCK-TOKEN --default-action Allow={} --captcha-config ImmunityTimeProperty={ImmunityTime=300} --challenge-config ImmunityTimeProperty={ImmunityTime=300} --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=ProductionWebACL --rules '[
{
"Name": "CaptchaLogin",
"Priority": 4,
"Statement": {
"ByteMatchStatement": {
"SearchString": "/login",
"FieldToMatch": {"UriPath": {}},
"TextTransformations": [{"Priority": 0, "Type": "LOWERCASE"}],
"PositionalConstraint": "STARTS_WITH"
}
},
"Action": {"Captcha": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "CaptchaLogin"
}
}
]' --region eu-west-1
2025-2026 Update: AWS WAF now supports configuring token domains for multi-domain applications, allowing a single CAPTCHA or Challenge token to be valid across multiple domains (e.g., example.com and api.example.com).
While managed rules cover common attack patterns, every application has unique endpoints, parameters, and business logic that require custom rules. AWS WAF supports regex pattern sets, string match conditions, size constraints, and geographic match statements.
Content-Type or custom API keys.../ or encoded variants beyond what the CRS catches.# Create a regex pattern set for blocking attack tools
aws wafv2 create-regex-pattern-set --name "BlockedUserAgents" --scope REGIONAL --regular-expression-list '[
{"RegexString": "(?i)(sqlmap|nikto|havij|nessus|masscan|zgrab)"},
{"RegexString": "(?i)(python-requests|go-http-client|java\/[0-9])"}
]' --region eu-west-1
# Use the regex pattern set in a rule
# Reference the ARN returned from the create command above
aws wafv2 update-web-acl --name "production-web-acl" --scope REGIONAL --id EXAMPLE-WEB-ACL-ID --lock-token EXAMPLE-LOCK-TOKEN --default-action Allow={} --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=ProductionWebACL --rules '[
{
"Name": "BlockAttackTools",
"Priority": 6,
"Statement": {
"RegexPatternSetReferenceStatement": {
"ARN": "arn:aws:wafv2:eu-west-1:123456789012:regional/regexpatternset/BlockedUserAgents/EXAMPLE-ID",
"FieldToMatch": {"SingleHeader": {"Name": "user-agent"}},
"TextTransformations": [{"Priority": 0, "Type": "NONE"}]
}
},
"Action": {"Block": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "BlockAttackTools"
}
}
]' --region eu-west-1
Best practice: Use text transformations (URL_DECODE, HTML_ENTITY_DECODE, LOWERCASE, COMPRESS_WHITE_SPACE) to normalize input before matching. Attackers routinely use encoding tricks to bypass pattern matching.
Combine AWS-managed IP reputation lists with custom IP sets and geographic restrictions to filter traffic before it reaches your application logic.
# Create a custom IP set for blocking
aws wafv2 create-ip-set --name "BlockedIPs" --scope REGIONAL --ip-address-version IPV4 --addresses '["203.0.113.0/24", "198.51.100.0/24"]' --region eu-west-1
# Create a geo-blocking rule (block traffic from specific countries)
# Use this as a rule statement in your web ACL
# GeoMatchStatement with CountryCodes: ["CN", "RU", "KP"]
aws wafv2 update-web-acl --name "production-web-acl" --scope REGIONAL --id EXAMPLE-WEB-ACL-ID --lock-token EXAMPLE-LOCK-TOKEN --default-action Allow={} --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=ProductionWebACL --rules '[
{
"Name": "GeoBlockRule",
"Priority": 7,
"Statement": {
"GeoMatchStatement": {
"CountryCodes": ["CN", "RU", "KP"]
}
},
"Action": {"Block": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "GeoBlockRule"
}
}
]' --region eu-west-1
Caveat: Geo-blocking is not a security measure on its own -- attackers use VPNs and compromised hosts in any country. Use it as one layer in defense-in-depth, combined with the Anonymous IP List managed rule group for more robust coverage.
Without logging, you cannot detect attacks, investigate incidents, tune rules, or prove compliance. AWS WAF supports three logging destinations: Amazon CloudWatch Logs, Amazon S3, and Amazon Kinesis Data Firehose.
# Enable WAF logging to CloudWatch Logs
aws wafv2 put-logging-configuration --logging-configuration '{
"ResourceArn": "arn:aws:wafv2:eu-west-1:123456789012:regional/webacl/production-web-acl/EXAMPLE-ID",
"LogDestinationConfigs": [
"arn:aws:logs:eu-west-1:123456789012:log-group:aws-waf-logs-production"
],
"RedactedFields": [
{"SingleHeader": {"Name": "authorization"}},
{"SingleHeader": {"Name": "cookie"}}
]
}'
# Create CloudWatch alarm for high block rate
aws cloudwatch put-metric-alarm --alarm-name "WAF-HighBlockRate" --metric-name BlockedRequests --namespace AWS/WAFV2 --statistic Sum --period 300 --threshold 1000 --comparison-operator GreaterThanThreshold --evaluation-periods 1 --alarm-actions arn:aws:sns:eu-west-1:123456789012:security-alerts --dimensions Name=WebACL,Value=production-web-acl Name=Rule,Value=ALL Name=Region,Value=eu-west-1
# Verify logging is enabled
aws wafv2 get-logging-configuration --resource-arn arn:aws:wafv2:eu-west-1:123456789012:regional/webacl/production-web-acl/EXAMPLE-ID
Security Hub Control: [WAF.11] AWS WAF web ACL logging should be enabled. [WAF.12] AWS WAF rules should have CloudWatch metrics enabled. Both are critical for visibility and incident response.
Best practice: Redact sensitive fields (Authorization headers, cookies, custom tokens) from logs to avoid storing credentials in plain text. Use log filter expressions to reduce log volume by excluding known-good traffic patterns.
AWS Firewall Manager lets you define WAF policies once and automatically deploy them across all accounts in your AWS Organization. Without it, each team manages their own web ACLs -- leading to inconsistent rules, missed protections on new resources, and compliance gaps.
# Create a Firewall Manager WAF policy
# Prerequisites: AWS Organizations, Firewall Manager admin account configured
aws fms put-policy --policy '{
"PolicyName": "OrgWideWAFPolicy",
"SecurityServicePolicyData": {
"Type": "WAFV2",
"ManagedServiceData": "{"type":"WAFV2","preProcessRuleGroups":[{"ruleGroupArn":null,"overrideAction":{"type":"NONE"},"managedRuleGroupIdentifier":{"vendorName":"AWS","managedRuleGroupName":"AWSManagedRulesCommonRuleSet"},"ruleGroupType":"ManagedRuleGroup","excludeRules":[]}],"postProcessRuleGroups":[],"defaultAction":{"type":"ALLOW"}}"
},
"ResourceType": "AWS::ElasticLoadBalancingV2::LoadBalancer",
"ResourceTags": [],
"ExcludeResourceTags": false,
"RemediationEnabled": true,
"IncludeMap": {
"ACCOUNT": ["123456789012", "987654321098"]
}
}'
# List compliance status across accounts
aws fms list-compliance-status --policy-id EXAMPLE-POLICY-ID
Best practice: Use Firewall Manager with a "pre-process" rule group strategy. Define baseline security rules (IP reputation, Core Rule Set) in the Firewall Manager policy as pre-process rules. Allow individual teams to add custom rules after the baseline. This ensures organizational security standards while preserving team autonomy.
AWS Shield Standard is automatically included with WAF at no extra cost and protects against common network-layer (L3/L4) DDoS attacks. Shield Advanced adds application-layer (L7) DDoS detection, the Shield Response Team (SRT), cost protection, and enhanced visibility.
# Subscribe to Shield Advanced
aws shield create-subscription
# Create a Shield Advanced protection for an ALB
aws shield create-protection --name "production-alb" --resource-arn arn:aws:elasticloadbalancing:eu-west-1:123456789012:loadbalancer/app/my-alb/EXAMPLE
# Enable automatic application-layer DDoS mitigation
aws shield enable-application-layer-automatic-response --resource-arn arn:aws:elasticloadbalancing:eu-west-1:123456789012:loadbalancer/app/my-alb/EXAMPLE --action Block={}
# Associate a health check for health-based detection
aws shield associate-health-check --protection-id EXAMPLE-PROTECTION-ID --health-check-arn arn:aws:route53:::healthcheck/EXAMPLE-HEALTH-CHECK-ID
# Grant SRT access to your WAF and Shield resources
aws shield associate-drt-role --role-arn arn:aws:iam::123456789012:role/AWSSRTAccess
Architecture best practice: Place CloudFront in front of your ALB and protect both with Shield Advanced. CloudFront absorbs volumetric attacks at the edge, while Shield Advanced on the ALB handles application-layer attacks that pass through. This layered approach is the architecture recommended by AWS for maximum DDoS resilience.
A WAF that is deployed and never tuned will either block legitimate traffic (false positives) or miss real attacks (false negatives). Continuous testing and tuning is essential for maintaining both security and availability.
OWASP ZAP, Nuclei, or the AWS WAF Testing Framework to run known attack payloads against your WAF-protected endpoints and verify rules trigger correctly.# Override a specific rule to Count (for false positive investigation)
# This overrides SizeRestrictions_BODY in the Common Rule Set
aws wafv2 update-web-acl --name "production-web-acl" --scope REGIONAL --id EXAMPLE-WEB-ACL-ID --lock-token EXAMPLE-LOCK-TOKEN --default-action Allow={} --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=ProductionWebACL --rules '[
{
"Name": "AWS-AWSManagedRulesCommonRuleSet",
"Priority": 1,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesCommonRuleSet",
"ExcludedRules": [
{"Name": "SizeRestrictions_BODY"}
]
}
},
"OverrideAction": {"None": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "AWSManagedRulesCommonRuleSet"
}
}
]' --region eu-west-1
# Check sampled requests to analyze blocked traffic
aws wafv2 get-sampled-requests --web-acl-arn arn:aws:wafv2:eu-west-1:123456789012:regional/webacl/production-web-acl/EXAMPLE-ID --rule-metric-name AWSManagedRulesCommonRuleSet --scope REGIONAL --time-window StartTime=2026-03-01T00:00:00Z,EndTime=2026-03-02T00:00:00Z --max-items 100
# Subscribe to managed rule group SNS topic for version updates
aws sns subscribe --topic-arn arn:aws:sns:us-east-1:248400274283:aws-managed-waf-rule-notifications --protocol email --notification-endpoint security-team@example.com
2025-2026 Update: AWS WAF now offers a simplified console experience that reduces security configuration steps by up to 80%. Pre-configured protection packs incorporate AWS security expertise and are continuously updated to address emerging threats. The unified dashboard provides consolidated security metrics, threat detection, and rule performance data -- making ongoing tuning significantly easier.
| Misconfiguration | Risk | Detection |
|---|---|---|
| Web ACL with no rules | All traffic passes through unfiltered | Security Hub: [WAF.10] |
| WAF logging disabled | No visibility into attacks or false positives | Security Hub: [WAF.11] |
| CloudWatch metrics disabled on rules | Cannot monitor rule effectiveness or trigger alarms | Security Hub: [WAF.12] |
| Rules deployed directly in Block mode | Legitimate traffic blocked, causing outages | Manual review of deployment process |
| No rate-based rules configured | Vulnerable to HTTP flood DDoS and brute force | Web ACL rule audit |
| Bot Control without JavaScript SDK | Reduced detection accuracy for sophisticated bots | Review application integration |
| Internet-facing ALBs without WAF | Direct exposure to application-layer attacks | AWS Config rule or Firewall Manager compliance |
| # | Practice | Priority |
|---|---|---|
| 1 | Deploy WAF on all internet-facing resources | Critical |
| 2 | Enable AWS Managed Rules (CRS + Known Bad Inputs) | Critical |
| 3 | Implement Bot Control | High |
| 4 | Configure rate-based rules | Critical |
| 5 | Enable Fraud Control (ATP/ACFP) | High |
| 6 | Use CAPTCHA and Challenge actions | Medium |
| 7 | Write custom rules with regex patterns | Medium |
| 8 | IP reputation lists and geo-blocking | High |
| 9 | Enable comprehensive logging and monitoring | Critical |
| 10 | Centralize WAF with Firewall Manager | High |
| 11 | Integrate Shield Advanced for DDoS protection | High |
| 12 | Continuously test and tune WAF rules | Critical |
This article is just the start. Get the full picture with our free whitepaper - 8 chapters covering IAM, S3, VPC, monitoring, agentic AI security, compliance, and a prioritized action plan with 50+ CLI commands.
Comprehensive guide to securing AWS Virtual Private Cloud. Covers Security Groups, NACLs, VPC Flow Logs, VPC Endpoints, Block Public Access, Encryption Controls, Network Firewall, Transit Gateway, and GuardDuty threat detection.
Comprehensive guide to securing AWS CloudTrail. Covers organization trails, KMS encryption, log integrity validation, S3 bucket hardening, CloudWatch integration, data events, Network Activity Events, Insights, SCP anti-tampering, CloudTrail Lake, and continuous audit.
Comprehensive guide to securing AWS Identity and Access Management. Covers MFA enforcement, least privilege, IAM Identity Center, SCPs, Access Analyzer, and credential management.