# Rules Engine for Firewall

A rule in Rules Engine for Firewall pairs criteria, which decide whether a request matches, with behaviors, which act on each request that matches. A rule whose criteria do not match applies nothing. To follow a request through the rules of a [firewall](/en/documentation/platform/firewall/), refer to [How Firewall works](/en/documentation/platform/firewall/how-it-works/).

A firewall rule decides whether a request reaches the application at all, because the firewall takes each request before the application does. A rule that chooses how the application serves an allowed request, such as its connector or cache setting, belongs to [Rules Engine for Applications](/en/documentation/platform/applications/rules-engine/).

---

## Rule fields

Each firewall holds its own list of rules. Azion Console shows them in the firewall's **Rules Engine** tab, where **Rule** opens the **Create Rule** drawer. The API serves them under `/v4/workspace/firewalls/<firewall-id>/request_rules`. A rule carries ten fields: five that a request sets, and five that the platform sets and returns.

| Field           | Type                                                      | Required  | Default            | Description                                                                                                                                                           |
| --------------- | --------------------------------------------------------- | --------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`            | integer                                                   | Read-only | —                  | The identifier every later call on the rule uses                                                                                                                      |
| `name`          | string, 1 to 250 characters                               | Yes       | —                  | The rule name. The Console renders it as the **Name** field and asks for a unique, descriptive name                                                                   |
| `description`   | string, up to 1,000 characters                            | No        | `""`               | A comment on the rule, shown in the **Description** column of the rule list. Past 1,000 characters, the Console shows `Description should not exceed 1000 characters` |
| `active`        | boolean                                                   | No        | `true`             | Whether the rule is active. The Console renders it as the **Active** switch in the **Status** section, and the rule list shows *Active* or *Inactive*                 |
| `criteria`      | array of 1 to 5 blocks, each an array of 1 to 10 criteria | Yes       | —                  | The conditions a request must meet. Each value is listed in Criteria                                                                                                  |
| `behaviors`     | array of behavior objects, at least 1                     | Yes       | —                  | What the rule does to a request that matches. Each value is listed in Behaviors                                                                                       |
| `order`         | integer                                                   | Read-only | Assigned at create | The rule's position in the firewall's list of rules                                                                                                                   |
| `last_editor`   | string                                                    | Read-only | —                  | The account that last changed the rule, shown in the **Last Editor** column                                                                                           |
| `last_modified` | date-time                                                 | Read-only | —                  | When the rule last changed, shown in the **Last Modified** column                                                                                                     |
| `created_at`    | date-time                                                 | Read-only | —                  | When the rule was created                                                                                                                                             |

The platform assigns `order` in creation order: the first rule on a firewall carries `0`, and the eleventh carries `10`. The Console rule list lets you reorder rules.

---

## Criteria

Criteria decide which requests a rule acts on. Each criterion names a variable, a comparison operator, and, for most operators, an argument to compare the variable with. The **Create Rule** drawer opens with one criterion already set to *Request Uri* and *starts with*, and `${request_uri}` `starts_with` `/` matches every request the workload receives.

The table lists the variables in Console order. Nine of them require Web Application Firewall (WAF) enabled on the firewall, `Network` requires Network Shield, and the other five need no Product.

| Variable                      | API variable                       | Requires       | What it holds                                                                                                                               | Example                            |
| ----------------------------- | ---------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
| Header Accept                 | `${header_accept}`                 | WAF            | The media types the client accepts in the response                                                                                          | `application/json`                 |
| Header Accept Encoding        | `${header_accept_encoding}`        | WAF            | The content encodings, usually compression algorithms, the client accepts in the response                                                   | `gzip`                             |
| Header Accept Language        | `${header_accept_language}`        | WAF            | The language the client expects                                                                                                             | `en-US`                            |
| Header Cookie                 | `${header_cookie}`                 | WAF            | The cookies the client sends with the request                                                                                               | `session_id=abc123`                |
| Header Origin                 | `${header_origin}`                 | WAF            | The origin of a cross-site request or a preflight request: a URI naming the server, with no path                                            | `https://example.com`              |
| Header Referer                | `${header_referer}`                | WAF            | The address of the document, or of the element in it, from which the request URI was obtained                                               | `https://example.com/landing-page` |
| Header User Agent             | `${header_user_agent}`             | WAF            | The string that identifies the client application, operating system, vendor, or version                                                     | `Mozilla/5.0`                      |
| Host                          | `${host}`                          | None           | In order of precedence: the hostname in the request line, the value of the `Host` header, or the name of the server that serves the request | `api.example.com`                  |
| Network                       | `${network}`                       | Network Shield | The client IP address, compared with the IP addresses and CIDR ranges, ASNs, or countries of a Network List                                 | `2`                                |
| Request Args                  | `${request_args}`                  | WAF            | The arguments in the query string of the request                                                                                            | `page=1`                           |
| Request Method                | `${request_method}`                | WAF            | The HTTP method of the request                                                                                                              | `POST`                             |
| Request Uri                   | `${request_uri}`                   | None           | The URI of the request                                                                                                                      | `/api/v1/`                         |
| Scheme                        | `${scheme}`                        | None           | The scheme of the request, HTTP or HTTPS                                                                                                    | `https`                            |
| Ssl Verification Status       | `${ssl_verification_status}`       | None           | The result of the client certificate validation                                                                                             | `CERTIFICATE_VERIFICATION_ERROR`   |
| Client Certificate Validation | `${client_certificate_validation}` | None           | The server process that authenticates the client's digital certificate                                                                      | `true`                             |

When the firewall lacks the Product a variable requires, the Console still lists the variable but does not let you select it. The label then carries a suffix, such as *Header Accept - required WAF* or *Network - required Network Shield*. The API refuses a rule that uses `${network}` on a firewall without Network Shield, with `400` and code `25047`.

`Network` compares the client IP address with a [Network List](/en/documentation/platform/firewall/network-shield/network-lists/). In the Console, the argument becomes the **Select a Network** list, which shows every Network List in the account and closes with **Create Network List**. In the API, the argument is the list ID sent as a JSON integer. For example, `2` is the Tor exit node list Azion provides, and any rule can reference it. The same ID sent as a string is refused with `400` and code `25042`.

Once a rule on the firewall uses `${network}`, Network Shield cannot be turned off on that firewall. The API answers `400` with code `24005` and names the rules that hold it.

`Request Args` holds nothing when the request has no query string, and an empty variable does not match. For example, a rule on `${request_args}` `matches` `.*` skips every POST that carries its payload in the body and none in the query string.

`Ssl Verification Status` takes one of three values, which the Console offers in the **Select an SSL Status** list:

| Value                            | API argument                     | Meaning                                   |
| -------------------------------- | -------------------------------- | ----------------------------------------- |
| *Success*                        | `SUCCESS`                        | The client certificate passed validation  |
| *Certificate Verification Error* | `CERTIFICATE_VERIFICATION_ERROR` | The client certificate was not valid      |
| *Missing Client Certificate*     | `MISSING_CLIENT_CERTIFICATE`     | The request carried no client certificate |

### Operators

A criterion compares its variable with its argument through an operator. The Console labels each operator in lowercase, and the API sends the value in the second column as `operator`.

| Operator            | API value             | The criterion matches when                                                                         | Argument           |
| ------------------- | --------------------- | -------------------------------------------------------------------------------------------------- | ------------------ |
| is equal            | `is_equal`            | The value equals the argument, compared character by character                                     | String             |
| is not equal        | `is_not_equal`        | The value is not exactly the argument                                                              | String             |
| starts with         | `starts_with`         | The value starts with the argument                                                                 | String             |
| does not start with | `does_not_start_with` | The value does not start with the argument                                                         | String             |
| matches             | `matches`             | The value matches the regular expression in the argument                                           | Regular expression |
| does not match      | `does_not_match`      | The value does not match the regular expression in the argument                                    | Regular expression |
| exists              | `exists`              | The variable has a value. `Request Args` exists when the query string carries an argument          | None               |
| does not exist      | `does_not_exist`      | The variable has no value. `Request Args` does not exist when the query string carries no argument | None               |

The Console hides the argument field for *exists* and *does not exist*, and replaces it with a list for `Network` and `Ssl Verification Status`.

`Network` is the exception to the table. Its *matches* and *does not match* carry the API values `is_in_list` and `is_not_in_list`, and both compare the client IP address with the Network List the argument names. The API refuses `matches` on `${network}` with `400` and code `25039`.

Each variable offers only some of the operators:

| Variable                                                                                                                       | Operators                                                                         |
| ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------- |
| Header Accept, Header Accept Encoding, Header Accept Language, Header Cookie, Header Origin, Header Referer, Header User Agent | matches, does not match                                                           |
| Host                                                                                                                           | is equal, is not equal, matches, does not match                                   |
| Network                                                                                                                        | matches (`is_in_list`), does not match (`is_not_in_list`)                         |
| Request Args                                                                                                                   | is equal, is not equal, matches, does not match, exists, does not exist           |
| Request Method                                                                                                                 | is equal, is not equal                                                            |
| Request Uri                                                                                                                    | is equal, is not equal, starts with, does not start with, matches, does not match |
| Scheme, Ssl Verification Status, Client Certificate Validation                                                                 | is equal, is not equal                                                            |

### Conditionals

Criteria combine inside blocks, and a rule holds from 1 to 5 blocks of 1 to 10 criteria each. Inside a block, the first criterion carries the conditional `if`, and each later criterion carries `and` or `or`. Blocks are joined by And, which the Console prints between them, so a request matches the rule only when it matches every block.

In the Console, **And** and **Or** add a criterion to a block, and **Add Criteria** adds a block. The Console disables **And** and **Or** at 10 criteria in a block, and **Add Criteria** at 5 blocks. The dividers between criteria show the conditional as If, And, or Or.

In the API, `criteria` is a list of blocks, and each block is a list of criterion objects. The rule in this example holds one block of two criteria. It denies a request only when the client IP address is in the Network List and the URI starts with `/ip-deny`:

```json
{
  "name": "ip-deny",
  "active": true,
  "criteria": [
    [
      { "variable": "${network}", "conditional": "if", "operator": "is_in_list", "argument": <network-list-id> },
      { "variable": "${request_uri}", "conditional": "and", "operator": "starts_with", "argument": "/ip-deny" }
    ]
  ],
  "behaviors": [{ "type": "deny" }]
}
```

The `<network-list-id>` placeholder stands for an integer with no quotes around it.

---

## Behaviors

Behaviors are what a rule does to a request that matches its criteria. The Console labels the first behavior row **Then** and each later row **And**, and **Add Behavior** adds a row. In the API, each behavior is an object with a `type`, and the behaviors that take settings carry them in `attributes`.

The Requires column names the Product the firewall must have enabled for the Console to offer the behavior. When it is off, the Console shows *Set WAF - required WAF* or *Run Function - required Functions* and does not let you select it. None of the six behaviors requires Network Shield.

| Behavior                      | API `type`            | Requires  | Attributes                                                     | What the client receives                                                                                   |
| ----------------------------- | --------------------- | --------- | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| Deny (403 Forbidden)          | `deny`                | None      | None                                                           | `403`, with Azion's default error page titled Forbidden                                                    |
| Drop (Close Without Response) | `drop`                | None      | None                                                           | No response: no status line and no body                                                                    |
| Set Rate Limit                | `set_rate_limit`      | None      | `type`, `limit_by`, `average_rate_limit`, `maximum_burst_size` | `429` for a request the limit does not admit, with the default error page titled Too Many Requests         |
| Set WAF                       | `set_waf`             | WAF       | `waf_id`, `mode`                                               | `400` for a request the rule set blocks in `blocking` mode, with the default error page titled Bad Request |
| Run Function                  | `run_function`        | Functions | `value`                                                        | Whatever the code of the function instance does with the request                                           |
| Set Custom Response           | `set_custom_response` | None      | `status_code`, `content_type`, `content_body`                  | The status code, `Content-Type`, and body the attributes set                                               |

The Console also limits how behaviors combine. Only *Run Function* and *Set WAF* can be followed by another behavior, and each appears at most once in a rule. Choosing *Deny (403 Forbidden)*, *Drop (Close Without Response)*, *Set Rate Limit*, or *Set Custom Response* removes every behavior row after it.

### Deny (403 Forbidden)

*Deny (403 Forbidden)* refuses the request with `403` and takes no attributes. The body is Azion's default error page titled Forbidden, about 10.6 KB of HTML, which shows the client IP address and the request ID. No response header names the firewall or the rule. In the API, the behavior is `{ "type": "deny" }`.

### Drop (Close Without Response)

*Drop (Close Without Response)* closes the request without answering it and takes no attributes. The client receives no status line and no body, immediately. A script that reads the status code sees `000`, and a browser shows a connection error. In the API, the behavior is `{ "type": "drop" }`.

Over HTTP/2, curl reports the reset stream and exits with code 92:

```text
curl: (92) HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)
```

Over HTTP/1.1 and plain HTTP, curl exits with code 52:

```text
curl: (52) Empty reply from server
```

### Set Rate Limit

*Set Rate Limit* caps the rate of the requests the rule matches. A request the rate and the burst do not admit receives `429`, with the default error page titled Too Many Requests. No response carries a rate-limit header, such as `retry-after` or an `x-ratelimit-` header.

| Attribute            | Console field          | Values                                                   | Description                                                                                                                            |
| -------------------- | ---------------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `type`               | **Rate Limit Type**    | `second` (*Req/s*) or `minute` (*Req/min*)               | The period the rate counts over. Defaults to `second`                                                                                  |
| `limit_by`           | **Limit By**           | `client_ip` (*Client IP address*) or `global` (*Global*) | Whether the rate counts per client IP address or across all requests. Required. The Console preselects *Client IP address*             |
| `average_rate_limit` | **Average Rate Limit** | Integer, at least 1                                      | The requests allowed per second or per minute, as `type` sets. Required                                                                |
| `maximum_burst_size` | **Maximum Burst Size** | Integer, at least 1                                      | The extra requests tolerated in a short peak. Applies to `second` only: the Console hides the field for *Req/min* and does not send it |

A rate limit of 10 requests per second per client IP address, with a burst of 10:

```json
{
  "type": "set_rate_limit",
  "attributes": { "type": "second", "limit_by": "client_ip", "average_rate_limit": 10, "maximum_burst_size": 10 }
}
```

For how the rate and the burst admit requests, refer to [How Firewall works](/en/documentation/platform/firewall/how-it-works/).

### Set WAF

*Set WAF* applies a WAF [rule set](/en/documentation/platform/firewall/waf/rules-set/) to the requests the rule matches, and requires WAF enabled on the firewall. A rule set runs only when a rule carries this behavior, and a rule carries at most one. Rule sets live in Azion Console under **Edge Libraries** > **WAF Rules**.

| Attribute | Console control       | Values                                           | Description                                                   |
| --------- | --------------------- | ------------------------------------------------ | ------------------------------------------------------------- |
| `waf_id`  | **Select a WAF**      | Integer                                          | The ID of the rule set to apply. Required                     |
| `mode`    | **Select a WAF mode** | `logging` (*Logging*) or `blocking` (*Blocking*) | Whether WAF refuses the requests the rule set flags. Required |

In `blocking` mode, a request the rule set blocks receives `400` with the default error page titled Bad Request, and no response header names WAF. In `logging` mode, WAF does not refuse the request. The API refuses a `set_waf` behavior with no `mode`, and any `mode` other than `logging` or `blocking`, including `learning`.

A behavior that applies a rule set in `blocking` mode:

```json
{ "type": "set_waf", "attributes": { "waf_id": <waf-rule-set-id>, "mode": "blocking" } }
```

### Run Function

*Run Function* runs a [function instance](/en/documentation/platform/firewall/functions-instances/) of the firewall on the requests the rule matches, and requires [Functions](/en/documentation/platform/functions/) enabled on the firewall. A firewall created through the API, the CLI, or the Console create page has Functions on, and the Console create drawer starts it off.

The attribute `value` holds the ID of the function instance, not of the function. Instances live in the firewall's **Functions Instances** tab. In the Console, **Select a Function** lists the active instances of this firewall, and **Create Function Instance** creates one from that list. A rule carries at most one *Run Function* behavior. To create an instance, refer to [Instantiate a function on a firewall](/en/documentation/guides/application-security/firewall-and-waf/instantiate-functions/).

A behavior that runs one function instance:

```json
{ "type": "run_function", "attributes": { "value": <function-instance-id> } }
```

For the code a firewall function runs, refer to [Functions for Firewall](/en/documentation/platform/firewall/functions/).

### Set Custom Response

*Set Custom Response* answers the request with the status code, content type, and body it sets, and requires no Product. The three settings go in `attributes`, as with every behavior that takes settings.

| Attribute      | Console field    | Values                       | Description                                                         |
| -------------- | ---------------- | ---------------------------- | ------------------------------------------------------------------- |
| `status_code`  | **Status code**  | Integer, 200 to 499          | The status code of the response. Required                           |
| `content_type` | **Content Type** | String, up to 255 characters | The value of the `Content-Type` header. Defaults to an empty string |
| `content_body` | **Content Body** | String, up to 500 characters | The body of the response. Defaults to an empty string               |

---

## API

Every rule operation is authenticated and sits under `https://api.azion.com/v4/workspace/firewalls/<firewall-id>/request_rules`. A request carries a personal token in the `Authorization` header under the `Token` scheme, and a request with a body also carries `Content-Type: application/json`.

| Operation                    | Method and path                           |
| ---------------------------- | ----------------------------------------- |
| Create a rule                | `POST /request_rules`                     |
| List the rules of a firewall | `GET /request_rules`                      |
| Retrieve a rule              | `GET /request_rules/{request_rule_id}`    |
| Update part of a rule        | `PATCH /request_rules/{request_rule_id}`  |
| Delete a rule                | `DELETE /request_rules/{request_rule_id}` |

A create and a delete answer `202`, and a read answers `200`.

This call creates a rule that denies every request whose URI starts with `/deny-test`:

```bash
curl --request POST \
  --url https://api.azion.com/v4/workspace/firewalls/<firewall-id>/request_rules \
  --header 'Accept: application/json' \
  --header 'Authorization: Token [TOKEN VALUE]' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "deny-test",
  "active": true,
  "criteria": [[{ "variable": "${request_uri}", "conditional": "if", "operator": "starts_with", "argument": "/deny-test" }]],
  "behaviors": [{ "type": "deny" }]
}'
```

The response carries `202` and a `state` of `pending`. The response excerpt keeps the fields the platform added:

```text
{"state":"pending","data":{"id":<rule-id>,…,"behaviors":[{"type":"deny"}],"description":"","order":0}}
```

The platform adds `description`, an empty string when the create sends none, and `order`, the rule's position on the firewall. The `id` in `data` is the handle every later call on the rule uses.

---

## CLI

Azion CLI manages rules under the `firewall-rule` noun. `azion create firewall-rule` takes only `--firewall-id` and `--file`, so the criteria and behaviors of a rule live in the same JSON body the API takes.

| Command                        | What it does                                                                                                        |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------- |
| `azion create firewall-rule`   | Creates a rule from a JSON file, with `--firewall-id` and `--file`                                                  |
| `azion list firewall-rule`     | Lists the rules of a firewall, with `--firewall-id`. `--details` adds the `LAST EDITOR` and `LAST MODIFIED` columns |
| `azion describe firewall-rule` | Returns one rule, with `--firewall-id` and `--rule-id`. `--format json` prints the full record                      |

This file describes a rule that denies requests from the countries in a Network List on `/cli-rule`:

```json
{
  "name": "cli-rule",
  "description": "Deny requests from countries in the list on /cli-rule.",
  "active": true,
  "criteria": [
    [
      { "variable": "${network}", "conditional": "if", "operator": "is_in_list", "argument": <network-list-id> },
      { "variable": "${request_uri}", "conditional": "and", "operator": "starts_with", "argument": "/cli-rule" }
    ]
  ],
  "behaviors": [
    { "type": "deny" }
  ]
}
```

Save it as `rule.json`, replace `<network-list-id>` with the list ID as an integer, and create the rule:

```bash
azion create firewall-rule --firewall-id <firewall-id> --file rule.json
```

```text
Created Firewall Rule with ID <rule-id>
```

---

## Errors

A rejected rule returns an `errors` array. Each entry carries a `code`, a `title`, a `detail`, the `status`, and a `source` pointer naming the field, and some entries add a `meta` object:

```json
{
  "errors": [
    {
      "code": "25047",
      "title": "Missing Required Modules",
      "detail": "variable \"${network}\" is not available for this Edge Firewall. It requires ANY of the following modules to be enabled: ['network_protection_enabled'].",
      "status": "400",
      "source": { "pointer": "/data/criteria" },
      "meta": {
        "message_prefix": "variable \"${network}\" is not available for this Edge Firewall.",
        "owner_modules": "ANY",
        "missing_required_modules": ["network_protection_enabled"]
      }
    }
  ]
}
```

| Code    | Title                                             | Status | What causes it                                                                                                                                                                                                                                | What to do                                                                                                      |
| ------- | ------------------------------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `10039` | Invalid Choice                                    | 400    | A value outside an enum: a `variable` such as `$(network)`, a spelling the v4 API specification lists, a `conditional` other than `if`, `and`, or `or`, or a `mode` such as `learning`. The `detail` reads `"<value>" is not a valid choice.` | Send a listed value. The `source` pointer names the field, such as `/data/criteria/0/0/conditional`             |
| `10059` | Required Field                                    | 400    | A required field is absent, such as a `set_waf` behavior with no `mode`, at `/data/behaviors/0/attributes/mode`                                                                                                                               | Add the field the `source` pointer names                                                                        |
| `24005` | Cannot Disable Firewall Network Protection Module | 400    | Turning Network Shield off on a firewall whose rules use `${network}`                                                                                                                                                                         | Change or delete the rules the `detail` lists, then turn Network Shield off                                     |
| `25030` | Entity Not Found                                  | 400    | A `${network}` argument naming a Network List that does not exist. The `detail` reads `Network List '<network-list-id>' not found.`                                                                                                           | Send the ID of an existing list                                                                                 |
| `25031` | Entity Not Active                                 | 400    | A `${network}` argument naming an inactive Network List. The `detail` reads `Network List '<network-list-id>' is not active.`                                                                                                                 | Activate the list, then create the rule                                                                         |
| `25036` | Invalid Informed WAF                              | 400    | A `waf_id` that names no rule set. The `detail` reads `The informed WAF (<waf-id>) is not valid.`, and `meta.waf_id` repeats the value                                                                                                        | Send the ID of an existing rule set                                                                             |
| `25039` | Invalid Operator                                  | 400    | `${network}` with the operator `matches`. The `detail` reads `The operator 'matches' is not valid for the '${network}' variable.`                                                                                                             | Send `is_in_list` or `is_not_in_list`                                                                           |
| `25042` | Invalid Operator Argument Type                    | 400    | A `${network}` argument sent as a string, such as `"57102"`, the type the v4 API specification gives it. The `detail` reads `The argument type '<network-list-id>' is not valid for the operator 'is_in_list'.`                               | Send the Network List ID as a JSON integer                                                                      |
| `25047` | Missing Required Modules                          | 400    | A rule using `${network}` on a firewall without Network Shield                                                                                                                                                                                | Enable Network Shield on the firewall, `modules.network_protection.enabled` set to `true`, then create the rule |

Two CLI rejections carry no code. A rule whose `run_function` behavior names a function instance that does not exist fails with `Error: failed to create the Firewall Rule: ["Function Instance '99999999' not found."]`, with the ID the file sent. A behavior written with `name` instead of `type` fails with `Error: Failed to decode the given 'json' file. Verify if the file format is JSON or fix its content according to the JSON format specification at https://www.json.org/json-en.html`, although the file is valid JSON: the key the platform reads is `type`.

---

## Related resources

- [How Firewall works](/en/documentation/platform/firewall/how-it-works.md): How a firewall runs its rules on a request, in which order, and when a behavior stops the rest.
- [Create a firewall rule](/en/documentation/guides/application-security/firewall-and-waf/work-with-rules-engine.md): The procedure that builds a rule in Azion Console.
- [Set a firewall's main settings](/en/documentation/guides/application-security/firewall-and-waf/firewall-configure-main-settings.md): Where to enable WAF, Network Shield, and Functions on a firewall before a rule uses them.
- [Create and apply a WAF rule set](/en/documentation/guides/application-security/firewall-and-waf/create-waf-rule-set.md): The procedure that creates a rule set and applies it with a Set WAF behavior.
