Trying Out AWS WAF's New "Pre-Parse Text Transformation" Feature with the CLI

Trying Out AWS WAF's New "Pre-Parse Text Transformation" Feature with the CLI

I configured the pre-parse text transformation added to AWS WAF in July 2026 using the AWS CLI. With a Count action and a WAF label, I check in WAF logs how semicolon-delimited and URL-encoded query strings affect rule matching.
2026.07.31

This page has been translated by machine translation. View original

Introduction

With the update on July 29, 2026, pre-parse text transformations became available in AWS WAF.

https://aws.amazon.com/about-aws/whats-new/2026/07/aws-waf/

Pre-parse text transformations are a feature that inserts normalization before the query string is parsed into key-value pairs. Conventional text transformations, when inspecting query arguments, operate on the values after parsing. Because of this, the handling of delimiters cannot be reflected in the parsing result. The official documentation explains that pre-parse text transformations can be applied only to statements that inspect SingleQueryArgument or AllQueryArguments.

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-preparse-transformation.html

With a rule without any transformation, neither the semicolon-delimited nor the URL-encoded semicolon-delimited query matched. The results after configuring pre-parse text transformations are as follows.

Query delimiter Applied pre-parse text transformation (Type)
Semicolon-delimited REPLACE_SEMICOLONS_WITH_AMPERSANDS
URL-encoded semicolon-delimited URL_DECODE → REPLACE_SEMICOLONS_WITH_AMPERSANDS

Verification Details

Configuring the Count action and rule labels

The verification environment is as follows.

Item Value
AWS CLI 2.36.11
Region ap-northeast-1
Web ACL scope REGIONAL
Backend ALB with fixed response
Log destination CloudWatch Logs

So that a block action would not interrupt the evaluation of subsequent rules, I set the test rules to the Count action and attached a WAF label for verification to each rule. WAF log records and HTTP requests were matched up using the query string as the key. Because Count does not block requests, I used labels and nonTerminatingMatchingRules as the basis for judgment instead of the HTTP status. I placed no blocking rules in the test Web ACL and configured only these Count rules for verification.

# Get the LockToken
LOCK_TOKEN=$(aws wafv2 get-web-acl \
  --name preparse-test-webacl \
  --id 11111111-2222-3333-4444-55555555aaaa \
  --scope REGIONAL \
  --region ap-northeast-1 \
  --query LockToken --output text)

# Set the Count rules and labels
aws wafv2 update-web-acl \
  --name preparse-test-webacl \
  --id 11111111-2222-3333-4444-55555555aaaa \
  --scope REGIONAL \
  --default-action '{"Allow":{}}' \
  --lock-token "$LOCK_TOKEN" \
  --region ap-northeast-1 \
  --visibility-config 'SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=preparse-test-webacl' \
  --rules file://rules.json

# Check the saved configuration
aws wafv2 get-web-acl \
  --name preparse-test-webacl \
  --id 11111111-2222-3333-4444-55555555aaaa \
  --scope REGIONAL \
  --region ap-northeast-1

For comparison, I also added two control rules (ControlNoPreparsePlain, ControlNoPreparseEncoded) that have the same matching conditions but no pre-parse transformation. Below are the two rules with pre-parse transformations, excerpted from the rule configuration passed to --rules.

[
  {
    "Name": "PreparseSemicolon",
    "Priority": 2,
    "Statement": {
      "ByteMatchStatement": {
        "SearchString": "cHJlcGFyc2UtcGxhaW4=",
        "FieldToMatch": { "SingleQueryArgument": { "Name": "target_plain" } },
        "TextTransformations": [ { "Priority": 0, "Type": "NONE" } ],
        "PositionalConstraint": "EXACTLY",
        "PreParseTextTransformations": [
          { "Priority": 0, "Type": "REPLACE_SEMICOLONS_WITH_AMPERSANDS" }
        ]
      }
    },
    "Action": { "Count": {} },
    "RuleLabels": [ { "Name": "test:preparse-semicolon" } ]
  },
  {
    "Name": "PreparseUrlDecodeSemicolon",
    "Priority": 4,
    "Statement": {
      "ByteMatchStatement": {
        "SearchString": "cHJlcGFyc2UtZW5jb2RlZA==",
        "FieldToMatch": { "SingleQueryArgument": { "Name": "target_encoded" } },
        "TextTransformations": [ { "Priority": 0, "Type": "NONE" } ],
        "PositionalConstraint": "EXACTLY",
        "PreParseTextTransformations": [
          { "Priority": 0, "Type": "URL_DECODE" },
          { "Priority": 1, "Type": "REPLACE_SEMICOLONS_WITH_AMPERSANDS" }
        ]
      }
    },
    "Action": { "Count": {} },
    "RuleLabels": [ { "Name": "test:preparse-url-decode-semicolon" } ]
  }
]

The SearchString values are Base64-encoded strings. cHJlcGFyc2UtcGxhaW4= corresponds to preparse-plain, and cHJlcGFyc2UtZW5jb2RlZA== corresponds to preparse-encoded. The excerpt omits the VisibilityConfig of each rule. After the update, I confirmed with get-web-acl that PreParseTextTransformations and RuleLabels had been saved.

Checking semicolon-delimited queries

Targeting the rule with REPLACE_SEMICOLONS_WITH_AMPERSANDS configured, I sent semicolon-delimited and ampersand-delimited requests.

Case Request query HTTP result Labels confirmed in WAF log Non-terminating matching rules
Semicolon-delimited q=safe;target_plain=preparse-plain 200 / ALLOW test:preparse-semicolon PreparseSemicolon
Normal ampersand-delimited (plain) q=safe&target_plain=preparse-plain 200 / ALLOW test:control-no-preparse-plain, test:preparse-semicolon ControlNoPreparsePlain, PreparseSemicolon

With the semicolon-delimited request, only the rule with the pre-parse transformation matched, and the control rule's label was not attached. With the ampersand-delimited request, both matched, so the labels show that the presence or absence of the transformation made the difference.

Below is an excerpt of the WAF log corresponding to the semicolon-delimited request. The labels value has a prefix that includes the Web ACL name.

{
  "action": "ALLOW",
  "terminatingRuleId": "Default_Action",
  "nonTerminatingMatchingRules": [
    { "ruleId": "PreparseSemicolon", "action": "COUNT", "ruleMatchDetails": [] }
  ],
  "httpRequest": { "args": "q=safe;target_plain=preparse-plain" },
  "labels": [
    { "name": "awswaf:123456789012:webacl:preparse-test-webacl:test:preparse-semicolon" }
  ]
}

Checking URL-encoded semicolons

Targeting the rule that chains URL_DECODE and REPLACE_SEMICOLONS_WITH_AMPERSANDS, I sent requests for the following two cases.

Case Request query HTTP result Labels confirmed in WAF log Non-terminating matching rules
URL-encoded semicolon-delimited q=safe%3Btarget_encoded=preparse-encoded 200 / ALLOW test:preparse-url-decode-semicolon PreparseUrlDecodeSemicolon
Normal ampersand-delimited (encoded) q=safe&target_encoded=preparse-encoded 200 / ALLOW test:control-no-preparse-encoded, test:preparse-url-decode-semicolon ControlNoPreparseEncoded, PreparseUrlDecodeSemicolon

With the request containing a URL-encoded semicolon, only the rule chaining decoding and replacement matched, and the control rule's label was not attached. With the ampersand-delimited request, both rules matched.

Summary

If you have been combining regular expressions or multiple rules to handle differences in query string delimiters and encoding, the newly added pre-parse text transformations may let you manage this more simply. Each pre-parse text transformation adds 10 WCU, but as long as the web ACL total is within the default quota of 1,500 WCU, you can use it at no additional charge.

Share this article

AWSのお困り事はクラスメソッドへ