AWS WAF Rules Deep Dive: Rate-Based, Geo, and Custom Rules

Bits Lovers
Written by Bits Lovers on
AWS WAF Rules Deep Dive: Rate-Based, Geo, and Custom Rules

WAF is one of those services where the default managed rules get you 80% of the way there. The last 20% is where it gets interesting.

Most teams attach AWSManagedRulesCommonRuleSet to their CloudFront distribution or ALB, set it to Block, and call it done. That works until you start seeing false positives blocking legitimate API traffic, or until an attacker figures out your rate limit is non-existent, or until your compliance team asks whether you’re actually inspecting POST body contents. That’s when you realize the managed rules are a starting point, not a complete policy.

This post goes through WAF rule types in enough detail to actually configure them correctly—rate-based rules for DDoS and credential stuffing, geo match for country-level controls, IP sets for allowlisting partners and blocklisting threat feeds, regex patterns for custom header and body inspection, and how to wire it all together in Terraform with proper logging to S3 or Kinesis.

How WAF Rules Work: Priority and Action Flow

Before getting into specific rule types, the action flow matters. WAF evaluates rules in priority order, lowest number first. The first rule that matches terminates evaluation for that request—subsequent rules don’t run. This means your allowlist rules need lower priority numbers than your blocklist rules.

Actions available per rule:

  • Allow: Pass the request through immediately, skip remaining rules
  • Block: Return a 403 (or a custom response) immediately
  • Count: Log the match but don’t block—useful for testing new rules before enforcing them
  • CAPTCHA: Serve a CAPTCHA challenge (requires the WAF JavaScript SDK in your frontend)
  • Challenge: AWS’s silent bot verification challenge

The default action applies when no rule matches—set this to Allow for most applications (your rules catch the bad traffic) or Block for API endpoints where you want an explicit allowlist.

One important nuance: rule groups have their own internal priority ordering. When you attach a managed rule group to a WAF ACL, AWS evaluates rules inside that group in their own order before moving to your next rule group. The OverrideAction at the group level can change group-matched Block actions to Count, which is how you shadow-test managed rules without breaking production.

Rate-Based Rules

Rate-based rules count requests from a given source over a five-minute window. When the count exceeds your threshold, WAF blocks the source until the rate drops below the threshold. The minimum threshold is 100 requests per five minutes.

The primary use case is DDoS protection and login throttling. Here’s a rule that throttles login endpoint abuse:

resource "aws_wafv2_web_acl" "main" {
  name  = "api-waf-acl"
  scope = "REGIONAL"

  default_action {
    allow {}
  }

  rule {
    name     = "RateLimitLoginEndpoint"
    priority = 10

    action {
      block {}
    }

    statement {
      rate_based_statement {
        limit              = 300
        aggregate_key_type = "IP"

        scope_down_statement {
          byte_match_statement {
            field_to_match {
              uri_path {}
            }
            positional_constraint = "STARTS_WITH"
            search_string         = "/api/v1/auth/login"
            text_transformation {
              priority = 0
              type     = "LOWERCASE"
            }
          }
        }
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "RateLimitLoginEndpoint"
      sampled_requests_enabled   = true
    }
  }
}

The scope_down_statement limits rate counting to only requests matching that URI prefix. Without it, the rule counts all requests from an IP regardless of endpoint—you’d end up blocking a user who makes 300 legitimate API calls while also hitting your login page a few times. Scope-down statements accept any valid WAF statement, including AND/OR combinations.

For more granular rate-limiting than IP-based aggregation, WAF supports custom aggregation keys in rate_based_statement. You can aggregate on a specific header value (like X-Forwarded-For or a custom API key header), a query string parameter, a cookie, or an HTTP method. This matters for APIs behind load balancers or CDNs that forward a single IP—aggregating on a user-identifying header gives you per-user rate limits instead of per-load-balancer limits.

rate_based_statement {
  limit              = 1000
  aggregate_key_type = "CUSTOM_KEYS"

  custom_key {
    header {
      name = "x-api-key"
      text_transformation {
        priority = 0
        type     = "NONE"
      }
    }
  }
}

Set rate limits differently per endpoint risk level. A public product listing endpoint might be fine at 5,000 requests per five minutes per IP. A password reset endpoint should be 10. An OTP verification endpoint should be 5. These are separate rules with separate scope-down statements, each targeting the specific path.

Geo Match Rules

Geo match rules let you allow or block requests based on the country associated with the source IP. WAF uses Amazon’s IP geolocation database, which it updates regularly. You can’t control which database version is in use.

Common patterns: block entire regions you don’t serve, or limit access to a specific set of countries for a partner-facing API.

rule {
  name     = "BlockHighRiskGeoRegions"
  priority = 20

  action {
    block {}
  }

  statement {
    geo_match_statement {
      country_codes = ["RU", "CN", "KP", "IR"]
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "BlockHighRiskGeoRegions"
    sampled_requests_enabled   = true
  }
}

A few operational notes. First, geo blocking by itself doesn’t stop sophisticated actors—they use exit nodes in unblocked countries. It does reduce noise from opportunistic scanners, which is still valuable. Second, if you’re using CloudFront, requests arrive at the edge from the viewer’s actual IP, so geo blocking works correctly. If you’re using an ALB, the source IP WAF sees is the actual client IP, which is also correct. Where this breaks is behind proxies that don’t set X-Forwarded-For properly.

For the inverted case—allowing only specific countries—use a NOT statement around a geo_match block, or more cleanly, use a geo match to allow traffic from your target countries at high priority (priority 1) combined with a catch-all block at low priority:

rule {
  name     = "AllowOnlyTargetCountries"
  priority = 5

  action {
    allow {}
  }

  statement {
    geo_match_statement {
      country_codes = ["US", "CA", "GB", "AU"]
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "AllowOnlyTargetCountries"
    sampled_requests_enabled   = true
  }
}

rule {
  name     = "BlockAllOtherGeo"
  priority = 99

  action {
    block {}
  }

  statement {
    not_statement {
      statement {
        geo_match_statement {
          country_codes = ["US", "CA", "GB", "AU"]
        }
      }
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "BlockAllOtherGeo"
    sampled_requests_enabled   = true
  }
}

This is more explicit than relying on default action for geo control.

IP Set Rules

IP sets let you allowlist or blocklist specific CIDR ranges. They’re the right tool for static partner allowlisting, internal office ranges, monitoring infrastructure, and known threat feed blocklists.

IP sets are separate resources from the ACL—you create them independently and reference them in rules. This makes them reusable across multiple ACLs and easier to update via automation without touching ACL rule logic.

resource "aws_wafv2_ip_set" "partner_allowlist" {
  name               = "partner-allowlist"
  scope              = "REGIONAL"
  ip_address_version = "IPV4"

  addresses = [
    "203.0.113.0/24",
    "198.51.100.42/32",
  ]
}

resource "aws_wafv2_ip_set" "threat_blocklist" {
  name               = "threat-blocklist"
  scope              = "REGIONAL"
  ip_address_version = "IPV4"

  addresses = [
    "192.0.2.0/24",
  ]
}

# Allow partner traffic before any inspection rules
rule {
  name     = "AllowPartnerIPs"
  priority = 1

  action {
    allow {}
  }

  statement {
    ip_set_reference_statement {
      arn = aws_wafv2_ip_set.partner_allowlist.arn
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "AllowPartnerIPs"
    sampled_requests_enabled   = true
  }
}

rule {
  name     = "BlockThreatFeed"
  priority = 2

  action {
    block {}
  }

  statement {
    ip_set_reference_statement {
      arn = aws_wafv2_ip_set.threat_blocklist.arn
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "BlockThreatFeed"
    sampled_requests_enabled   = true
  }
}

IP sets support up to 10,000 addresses per set. For threat feed automation, a Lambda function that pulls from an external feed (Spamhaus, AlienVault, your own threat intel) and calls update_ip_set on a schedule is the standard pattern. The WAF API supports atomic updates to IP sets—you pass the entire new address list and the current lock token in a single call. See the AWS API Gateway WAF and Nginx Zero Trust setup post for how this integrates with an API Gateway + Nginx architecture.

One gotcha: IP sets are regional or CloudFront-scoped. A REGIONAL IP set can’t be used in a CLOUDFRONT ACL. If you’re protecting both CloudFront and ALB endpoints, you need separate IP set resources in both scopes, and you need to keep them synchronized.

Regex Pattern Sets

Regex pattern sets let you match against request components using regular expressions. You can inspect URI paths, query strings, headers, cookies, and HTTP body content.

resource "aws_wafv2_regex_pattern_set" "sql_inject_patterns" {
  name  = "custom-sqli-patterns"
  scope = "REGIONAL"

  regular_expression {
    regex_string = "(?i)(union|select|insert|update|delete|drop|create|alter)\\s+(all|distinct|from|into|table|database)"
  }

  regular_expression {
    regex_string = "(?i)(\\bor\\b|\\band\\b)\\s+[\\w\\s]+=\\s*[\\w\\s]+"
  }
}

rule {
  name     = "CustomSQLiPatterns"
  priority = 30

  action {
    block {}
  }

  statement {
    regex_pattern_set_reference_statement {
      arn = aws_wafv2_regex_pattern_set.sql_inject_patterns.arn

      field_to_match {
        body {
          oversize_handling = "MATCH"
        }
      }

      text_transformation {
        priority = 0
        type     = "URL_DECODE"
      }

      text_transformation {
        priority = 1
        type     = "HTML_ENTITY_DECODE"
      }
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "CustomSQLiPatterns"
    sampled_requests_enabled   = true
  }
}

The text_transformation chain matters here. Attackers encode payloads specifically to evade pattern matching—URL encoding, HTML entity encoding, base64. Applying URL_DECODE and HTML_ENTITY_DECODE before pattern matching catches encoded variants. WAF applies transformations in priority order, so the order you specify them is the order they execute.

Body inspection has a size limit. WAF only inspects the first 8KB of request bodies by default (configurable up to 64KB with an additional cost). The oversize_handling option controls what happens when a body exceeds the limit: MATCH (treat as matching the rule), NO_MATCH (skip this rule), or CONTINUE (evaluate against whatever portion was inspected). For security-sensitive endpoints, MATCH is the conservative choice—if you can’t inspect the whole body, treat it as suspicious.

AWS Managed Rule Groups

Managed rule groups are AWS-maintained collections of rules you can attach to your ACL without defining individual rules. They cover common attack categories and AWS updates them as new threats emerge without you changing your ACL configuration.

The main groups for most applications:

  • AWSManagedRulesCommonRuleSet: OWASP Top 10 coverage, broad attack surface
  • AWSManagedRulesKnownBadInputsRuleSet: Log4Shell, path traversal, Spring4Shell
  • AWSManagedRulesSQLiRuleSet: SQL injection patterns across URI, body, headers
  • AWSManagedRulesLinuxRuleSet: Linux-specific attacks including LFI
  • AWSManagedRulesAmazonIpReputationList: IPs with poor reputation in AWS’s telemetry
rule {
  name     = "AWSManagedRulesCommonRuleSet"
  priority = 50

  override_action {
    none {}
  }

  statement {
    managed_rule_group_statement {
      name        = "AWSManagedRulesCommonRuleSet"
      vendor_name = "AWS"

      rule_action_override {
        name = "SizeRestrictions_BODY"
        action_to_use {
          count {}
        }
      }

      rule_action_override {
        name = "NoUserAgent_HEADER"
        action_to_use {
          count {}
        }
      }
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "AWSManagedRulesCommonRuleSet"
    sampled_requests_enabled   = true
  }
}

rule {
  name     = "AWSManagedRulesSQLiRuleSet"
  priority = 55

  override_action {
    none {}
  }

  statement {
    managed_rule_group_statement {
      name        = "AWSManagedRulesSQLiRuleSet"
      vendor_name = "AWS"
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "AWSManagedRulesSQLiRuleSet"
    sampled_requests_enabled   = true
  }
}

The rule_action_override blocks let you downgrade specific rules within a managed group from Block to Count. SizeRestrictions_BODY often causes false positives on APIs that accept large JSON payloads—an upload endpoint sending a 10KB configuration object is legitimate and shouldn’t be blocked. NoUserAgent_HEADER blocks requests without a User-Agent header, which breaks many server-to-server API clients and monitoring tools. Run these in Count mode first, check the logs for what you’d be blocking, then decide whether to enforce.

If you’re running an API behind API Gateway, the Cognito JWT Authorizer post covers layering authorization checks before requests even reach WAF-protected integration endpoints.

Custom Rules with JSON Body Inspection

For APIs that accept JSON bodies, WAF’s JSON parsing capability lets you write rules that inspect specific fields within the JSON structure rather than treating the body as raw bytes.

rule {
  name     = "InspectLoginJSONBody"
  priority = 35

  action {
    block {}
  }

  statement {
    sqli_match_statement {
      field_to_match {
        json_body {
          match_pattern {
            included_paths = ["/username", "/email", "/password"]
          }
          match_scope           = "VALUE"
          invalid_fallback_behavior = "MATCH"
          oversize_handling     = "MATCH"
        }
      }

      text_transformation {
        priority = 0
        type     = "URL_DECODE"
      }

      text_transformation {
        priority = 1
        type     = "HTML_ENTITY_DECODE"
      }
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "InspectLoginJSONBody"
    sampled_requests_enabled   = true
  }
}

match_scope = "VALUE" inspects only JSON values, not keys. match_scope = "ALL" inspects both keys and values—useful if you’re worried about prototype pollution attacks or key injection. invalid_fallback_behavior = "MATCH" treats malformed JSON as matching the rule—if someone sends malformed JSON to your API endpoint, that’s suspicious by default.

The included_paths list uses JSONPointer syntax. This is more precise and less error-prone than applying the full regex match to the entire body, and it doesn’t have false positives from field names that happen to contain SQL keywords.

Complete Terraform WAF ACL

Putting the pieces together into a deployable ACL for a regional API:

resource "aws_wafv2_web_acl" "api_waf" {
  name        = "${var.environment}-api-waf"
  description = "WAF ACL for ${var.environment} API endpoints"
  scope       = "REGIONAL"

  default_action {
    allow {}
  }

  # 1. Allow known partners unconditionally
  rule {
    name     = "AllowPartnerIPs"
    priority = 1
    action { allow {} }
    statement {
      ip_set_reference_statement {
        arn = aws_wafv2_ip_set.partner_allowlist.arn
      }
    }
    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "AllowPartnerIPs"
      sampled_requests_enabled   = true
    }
  }

  # 2. Block known bad IPs immediately
  rule {
    name     = "BlockThreatFeed"
    priority = 2
    action { block {} }
    statement {
      ip_set_reference_statement {
        arn = aws_wafv2_ip_set.threat_blocklist.arn
      }
    }
    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "BlockThreatFeed"
      sampled_requests_enabled   = true
    }
  }

  # 3. Geo restrictions
  rule {
    name     = "BlockHighRiskGeo"
    priority = 10
    action { block {} }
    statement {
      geo_match_statement {
        country_codes = var.blocked_countries
      }
    }
    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "BlockHighRiskGeo"
      sampled_requests_enabled   = true
    }
  }

  # 4. Login endpoint rate limiting
  rule {
    name     = "RateLimitLogin"
    priority = 20
    action { block {} }
    statement {
      rate_based_statement {
        limit              = 300
        aggregate_key_type = "IP"
        scope_down_statement {
          byte_match_statement {
            field_to_match { uri_path {} }
            positional_constraint = "STARTS_WITH"
            search_string         = "/api/v1/auth"
            text_transformation {
              priority = 0
              type     = "LOWERCASE"
            }
          }
        }
      }
    }
    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "RateLimitLogin"
      sampled_requests_enabled   = true
    }
  }

  # 5. Global rate limit
  rule {
    name     = "GlobalRateLimit"
    priority = 25
    action { block {} }
    statement {
      rate_based_statement {
        limit              = 10000
        aggregate_key_type = "IP"
      }
    }
    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "GlobalRateLimit"
      sampled_requests_enabled   = true
    }
  }

  # 6. AWS managed rules (SQLi, CRS)
  rule {
    name     = "AWSManagedRulesCommonRuleSet"
    priority = 50
    override_action { none {} }
    statement {
      managed_rule_group_statement {
        name        = "AWSManagedRulesCommonRuleSet"
        vendor_name = "AWS"
        rule_action_override {
          name = "SizeRestrictions_BODY"
          action_to_use { count {} }
        }
      }
    }
    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "AWSManagedRulesCommonRuleSet"
      sampled_requests_enabled   = true
    }
  }

  rule {
    name     = "AWSManagedRulesSQLiRuleSet"
    priority = 55
    override_action { none {} }
    statement {
      managed_rule_group_statement {
        name        = "AWSManagedRulesSQLiRuleSet"
        vendor_name = "AWS"
      }
    }
    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "AWSManagedRulesSQLiRuleSet"
      sampled_requests_enabled   = true
    }
  }

  rule {
    name     = "AWSManagedRulesKnownBadInputsRuleSet"
    priority = 60
    override_action { none {} }
    statement {
      managed_rule_group_statement {
        name        = "AWSManagedRulesKnownBadInputsRuleSet"
        vendor_name = "AWS"
      }
    }
    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "AWSManagedRulesKnownBadInputsRuleSet"
      sampled_requests_enabled   = true
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "${var.environment}-api-waf"
    sampled_requests_enabled   = true
  }
}

# Associate with ALB
resource "aws_wafv2_web_acl_association" "api" {
  resource_arn = aws_lb.api.arn
  web_acl_arn  = aws_wafv2_web_acl.api_waf.arn
}

CloudWatch Metrics and Logging

WAF emits per-rule CloudWatch metrics under the AWS/WAFV2 namespace when cloudwatch_metrics_enabled = true on each rule. The key metric is BlockedRequests—set alarms on this per rule to detect spikes. A sudden spike in RateLimitLogin blocked requests at 3am is worth waking someone up for.

For full request logging, WAF supports three destinations: S3, CloudWatch Logs, and Kinesis Data Firehose. Kinesis Firehose is the most flexible because it can deliver to S3 (with optional Glue/Athena querying), Elasticsearch/OpenSearch, or Redshift.

resource "aws_wafv2_web_acl_logging_configuration" "api" {
  log_destination_configs = [aws_kinesis_firehose_delivery_stream.waf_logs.arn]
  resource_arn            = aws_wafv2_web_acl.api_waf.arn

  logging_filter {
    default_behavior = "DROP"

    filter {
      behavior = "KEEP"

      condition {
        action_condition {
          action = "BLOCK"
        }
      }

      requirement = "MEETS_ANY"
    }

    filter {
      behavior = "KEEP"

      condition {
        label_name_condition {
          label_name = "awswaf:managed:aws:core-rule-set:"
        }
      }

      requirement = "MEETS_ANY"
    }
  }
}

The logging_filter block is important for cost control. WAF logs every request by default, which on a high-traffic application means billions of log records per day. The filter above keeps only blocked requests and requests that matched managed rule labels—everything that actually matters for security analysis. Pass-through traffic that hits no rules goes to DROP (not logged). This can reduce log volume by 90%+ on typical API traffic.

Log fields include the terminating rule name, matched rule labels from managed groups, country code, client IP, URI, method, and the matched portion of the request. For compliance auditing and incident response, the combination of Kinesis → S3 → Athena is solid. For encryption of WAF logs, use KMS customer-managed keys with appropriate key policies—WAF supports SSE-KMS on S3 log destinations out of the box.

If you need real-time alerting on WAF blocks, route the Kinesis stream to a Lambda function that publishes to SNS for high-severity matches (SQLI matches on production endpoints, for instance).

Bot Control and Fraud Prevention

AWS WAF has two premium add-ons that go beyond the base rule types.

Bot Control adds a managed rule group (AWSManagedRulesBotControlRuleSet) that classifies traffic by bot type. It distinguishes between verified good bots (Googlebot, legitimate monitoring), bots it can identify but hasn’t verified, and unknown bots. Pricing is based on requests inspected by Bot Control, not per rule—at $10 per million requests on top of WAF base costs, it adds up fast on high-traffic applications.

The targeted inspection mode (the newer tier) adds JavaScript and browser fingerprinting challenges that catch headless browsers and automation tools that spoof standard headers. If you’re dealing with credential stuffing or scraping that evades header-based detection, targeted mode is effective. Standard mode is cheaper and handles obvious bot traffic but misses sophisticated automation.

Fraud Control - Account Takeover Prevention (ATP) is a separate managed rule group that specifically targets credential stuffing and account takeover patterns. It integrates with your login endpoint, inspects username/password field structures, and uses AWS’s threat intelligence on known compromised credentials. It also implements token-based client verification that persists across requests in a session, making it harder for bots to rotate IPs and bypass rate limits.

For most teams, Bot Control standard mode covers the bulk of bot traffic. ATP is worth the cost if you’re seeing active credential stuffing campaigns that rate-based rules alone aren’t stopping. For rate limiting approaches at the infrastructure level before WAF inspection, the Nginx rate limiting and whitelisting guide covers the layer below WAF.

Cost Optimization

WAF pricing has two components: Web ACL charges ($5/month per ACL) and request processing charges ($0.60 per million requests for the first 10 billion/month). Managed rule groups add $1 per million requests per group. Bot Control adds $10 per million requests (targeted mode is higher).

A few places where costs escape:

Multiple ACLs for the same scope: Some teams create separate ACLs per application when a single ACL with different rule scope-down statements covers the same traffic. One ACL with scope-down statements on URIs is cheaper than five ACLs with one rule each.

Logging all requests: As mentioned above, use logging filters. Logging everything to CloudWatch Logs at high request volumes generates significant CWL ingest costs on top of WAF costs. Kinesis Firehose to S3 is substantially cheaper for high-volume logging.

Managed rule groups on low-confidence traffic: If 40% of your requests come from known good sources (authenticated users, partner APIs), consider putting those through the allowlist path (which terminates evaluation before managed rule groups run) rather than running every request through every managed rule group. The cost saving depends on your traffic mix, but on high-volume applications it’s meaningful.

Bot Control scope-down: You can scope Bot Control to specific paths. If you only care about bot traffic on your public listing pages and checkout flow, scope Bot Control to those paths rather than running it on every request including authenticated API calls from your own mobile app.

Request sampling in visibility_config (sampled_requests_enabled = true) is free—it’s a subset of requests that AWS captures for the console’s sampled requests view. It doesn’t affect CloudWatch metrics or logging configuration.

Putting It Together

The operational pattern that works well is starting with managed rules in Count mode behind a monitoring dashboard, watching for false positives for a week, then flipping to Block mode after overriding the rules that generate false positives for your specific application. Don’t start in Block mode—you’ll break something.

Rule priority ordering that tends to work for most APIs: IP allowlists at 1-5, IP blocklists at 6-10, geo blocks at 10-20, rate limits at 20-40, custom content inspection at 40-60, managed rule groups at 60-90. Leave gaps in the numbers so you can insert rules later without renumbering everything.

The WAF console’s sampled requests view is genuinely useful during the tuning phase. For each blocked rule, you can see the actual request headers and the specific match that triggered the block. This is how you discover that your SizeRestrictions_BODY rule is blocking legitimate file uploads, or that GenericRFI_BODY is matching innocuous configuration parameters that happen to contain file path strings.

After WAF is in Block mode on production, the ongoing work is mostly log review for false positives and rate limit tuning as your traffic patterns change. The managed rule groups handle new CVEs and attack patterns automatically. The custom rules and IP sets require occasional maintenance as your application evolves and your threat landscape changes.

For secrets and API key management alongside your WAF configuration, Secrets Manager rotation patterns are worth reading alongside this—WAF rules that inspect API keys and the key rotation logic that makes those keys short-lived complement each other well in a defense-in-depth strategy.

Bits Lovers

Bits Lovers

Professional writer and blogger. Focus on Cloud Computing.

Comments

comments powered by Disqus