# Rules Engine for Applications

Rules Engine for Applications holds the conditional logic of an [application](/en/documentation/platform/applications/). Each rule tests a request against its criteria, the *if* of the rule, and runs its behaviors, the *then*, only when the request matches. Every rule belongs to one phase: it acts on the request the user sends, or on the response the user receives. Some variables and behaviors need a Product enabled on the application, and each table on this page names it. For the order in which an application runs its rules and behaviors, refer to [How Applications works](/en/documentation/platform/applications/how-it-works/).

An application rule decides how the application handles a request that reached it, such as the connector or the cache setting the request uses. When the workload's deployment names a firewall, the firewall decides earlier whether the request reaches the application at all. For its rules, refer to [Rules Engine for Firewall](/en/documentation/platform/firewall/rules-engine/).

---

## Rule fields

An application keeps its rules in the **Rules Engine** tab of Azion Console, where **+ Rule** creates one. A new application has no rules. In the API, a rule is a JSON object: a request sets five of its fields, and the platform sets and returns the other five.

| Field           | Type                                                      | Required  | Description                                                                                                                                                                                         |
| --------------- | --------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`            | integer                                                   | Read-only | The identifier every later call on the rule uses                                                                                                                                                    |
| `name`          | string, 1 to 250 characters                               | Yes       | The name the rule list shows. The Console renders it as **Name**, in the **General** section. Give each rule a unique name                                                                          |
| `description`   | string, up to 1,000 characters                            | No        | A comment shown in the rule list, rendered as **Description** in **General**. Defaults to an empty string. Past 1,000 characters, the Console shows `Description should not exceed 1000 characters` |
| `active`        | boolean                                                   | No        | Whether the rule runs. Defaults to `true`. The Console renders it as the **Active** switch in the **Status** section                                                                                |
| `criteria`      | array of 1 to 5 groups, each an array of 1 to 10 criteria | Yes       | The conditions a request must meet. Criteria lists every variable and operator                                                                                                                      |
| `behaviors`     | array of 1 to 10 behavior objects                         | Yes       | What the rule does when the request matches. Behaviors lists every one                                                                                                                              |
| `order`         | integer, 0 to 199                                         | Read-only | The position of the rule in the list of its phase                                                                                                                                                   |
| `last_editor`   | string                                                    | Read-only | The account that last changed the rule                                                                                                                                                              |
| `last_modified` | date-time                                                 | Read-only | When the rule last changed                                                                                                                                                                          |
| `created_at`    | date-time                                                 | Read-only | When the rule was created                                                                                                                                                                           |

The platform assigns `order` as rules are created: the first rule of a phase carries `0`, and the next one carries `1`. The Console rule list lets you move a rule, and a position past the end of the list places the rule last. The API reorders a phase with one call, which the API section lists.

---

## Phases

A rule runs in the phase chosen in the **Phase** section when the rule is created, and that choice is final. To run the same logic in the other phase, create a new rule there. The Console groups the rule list by phase, and the API keeps each phase in its own collection.

| Phase          | What its rules act on                             | API collection                                               |
| -------------- | ------------------------------------------------- | ------------------------------------------------------------ |
| Request Phase  | The request the user sends to the application     | `/v4/workspace/applications/<application-id>/request_rules`  |
| Response Phase | The response the application delivers to the user | `/v4/workspace/applications/<application-id>/response_rules` |

Each phase offers its own set of variables and behaviors. A value that exists only once the origin answers, such as `${status}`, is readable only in the Response Phase. The Phases column of the variable table and the two API columns of the behavior table say which phase accepts each one.

---

## Criteria

Criteria decide which requests a rule acts on. A criterion names a variable, an operator, and, for most operators, an argument to compare the variable with. In the API, a criterion is an object with four keys: `variable`, `operator`, `conditional`, and `argument`.

This criterion matches a request from a desktop browser, with a regular expression on the `User-Agent` header:

```json
[[{ "variable": "${http_user_agent}", "conditional": "if", "operator": "matches", "argument": "(Chrome|Mozilla)" }]]
```

### Variables

A variable holds one value of the request or of the response, and the Phases column names where a criterion can read it. `${request_uri}` and `${device_group}` require [Application Accelerator](/en/documentation/platform/applications/application-accelerator/settings/) on the application. The API refuses a rule with `${request_uri}` on an application without it, with `400` and code `25047`. `${uri}` needs no Product, so it is the URI variable for an application without Application Accelerator.

Six entries are families: replace `name` with the argument, cookie, or header to read.

| Variable                       | What it holds                                                                                                                                                                                                                                         | Example                                                                                                                   | Phases            |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ----------------- |
| `${arg_name}`                  | The value of the query-string argument `name`                                                                                                                                                                                                         | `${arg_search}` holds `test` for `/path?search=test`                                                                      | Request, Response |
| `${args}`                      | Every argument name and value in the query string                                                                                                                                                                                                     | `${args}` holds `search=test` for `/path?search=test`                                                                     | Request, Response |
| `${cookie_name}`               | The value of the cookie `name`                                                                                                                                                                                                                        | `${cookie_icl_current_language}` holds `pt-br` for `icl_current_language = pt-br`                                         | Request, Response |
| `${device_group}`              | The name of the [device group](/en/documentation/platform/applications/device-groups/) the request matches, from the **Device Groups** tab of the application. Requires Application Accelerator                                                       | `Mobile`                                                                                                                  | Request, Response |
| `${domain}`                    | The host name or `Host` header of the request, as `${host}` reads it, without the last subdomain after the second-level domain                                                                                                                        | `blog.domain.com` for `az.blog.domain.com`                                                                                | Request, Response |
| `${geoip_city}`                | The city name, from the geolocation base `geoip_city`                                                                                                                                                                                                 | `Sao Paulo`                                                                                                               | Request, Response |
| `${geoip_city_continent_code}` | The two-letter continent code, from the geolocation base `geoip_city`                                                                                                                                                                                 | `EU`, for Europe                                                                                                          | Request, Response |
| `${geoip_city_country_code}`   | The two-letter country code, from the geolocation base `geoip_city`                                                                                                                                                                                   | `IN`, for India                                                                                                           | Request, Response |
| `${geoip_city_country_name}`   | The country name, from the geolocation base `geoip_city`                                                                                                                                                                                              | `United States`                                                                                                           | Request, Response |
| `${geoip_continent_code}`      | The two-letter continent code                                                                                                                                                                                                                         | `NA`, for North America                                                                                                   | Request, Response |
| `${geoip_country_code}`        | The two-letter country code, from the geolocation base `geoip_country`                                                                                                                                                                                | `RU`, for Russia                                                                                                          | Request, Response |
| `${geoip_country_name}`        | The country name, from the geolocation base `geoip_country`                                                                                                                                                                                           | `France`                                                                                                                  | Request, Response |
| `${geoip_region}`              | The two-letter region code                                                                                                                                                                                                                            | `FL`, for Florida                                                                                                         | Request, Response |
| `${geoip_region_name}`         | The region name, from the geolocation base `geoip_region`                                                                                                                                                                                             | `Ontario`                                                                                                                 | Request, Response |
| `${host}`                      | In order of precedence: the host name in the request line, the value of the `Host` header, or the name of the server that serves the request                                                                                                          | `blog.domain.com`                                                                                                         | Request, Response |
| `${http_name}`                 | The value of the request header `name`, written in lowercase with each hyphen replaced by an underscore. `name` must be a valid [HTTP request header](https://developer.mozilla.org/en-US/docs/Glossary/Request_header)                               | `${http_accept}` holds `image/webp,image/apng` for `Accept: image/webp,image/apng`                                        | Request, Response |
| `${remote_addr}`               | The IP address of the client that sends the request                                                                                                                                                                                                   | `200.10.2.50`                                                                                                             | Request, Response |
| `${remote_port}`               | The port the client uses in the URL of its request                                                                                                                                                                                                    | `443`                                                                                                                     | Request, Response |
| `${remote_user}`               | The user name sent through basic authentication, when the request carries one                                                                                                                                                                         | `username`                                                                                                                | Request, Response |
| `${request}`                   | The original first line of the request: the method, the URI, and the HTTP version                                                                                                                                                                     | `GET /path HTTP/2.0`                                                                                                      | Request, Response |
| `${request_body}`              | The body of the request                                                                                                                                                                                                                               | `{"name": "azion", "action": "login"}`                                                                                    | Request, Response |
| `${request_method}`            | The HTTP method of the request                                                                                                                                                                                                                        | `GET`                                                                                                                     | Request, Response |
| `${request_uri}`               | The complete URI of the request, query string included, with special UTF-8 characters URL encoded. Requires Application Accelerator                                                                                                                   | `/path?var=value%20of%20var`                                                                                              | Request, Response |
| `${scheme}`                    | The scheme of the request                                                                                                                                                                                                                             | `https`                                                                                                                   | Request, Response |
| `${sent_http_name}`            | The value of the response header `name`, written in lowercase with each hyphen replaced by an underscore                                                                                                                                              | `${sent_http_content_length}` holds `9593` for `Content-Length: 9593`                                                     | Response          |
| `${server_addr}`               | The IP address of the server that receives the request                                                                                                                                                                                                | `200.0.0.0`                                                                                                               | Request           |
| `${server_port}`               | The port of the server that receives the request                                                                                                                                                                                                      | `8080`                                                                                                                    | Request           |
| `${status}`                    | The status code of the response                                                                                                                                                                                                                       | `200`                                                                                                                     | Response          |
| `${tcpinfo_rtt}`               | The round-trip time (RTT) of the client's TCP connection, in microseconds                                                                                                                                                                             | `24763`                                                                                                                   | Response          |
| `${upstream_addr}`             | The IP address and port of the origin that answered. Several origins are separated by commas. After an internal redirect from one group of servers to another, started by an `X-Accel-Redirect` or an error page, the groups are separated by a colon | `192.168.1.1:80, 192.168.1.2:80`, or `192.168.1.1:80, 192.168.1.2:80 : 192.168.10.1:80, 192.168.10.2:80` after a redirect | Response          |
| `${upstream_cookie_name}`      | The value of the cookie `name` that the origin sends in `Set-Cookie`. When several origins answer one request, only the cookies of the last one are kept                                                                                              | `${upstream_cookie_uuid}` holds `12345` for `Set-Cookie: uuid = 12345`                                                    | Response          |
| `${upstream_http_name}`        | The value of the header `name` that the origin sends, written in lowercase with each hyphen replaced by an underscore. When several origins answer one request, only the headers of the last one are kept                                             | `${upstream_http_server}` holds `UploadServer` for `Server: UploadServer`                                                 | Response          |
| `${upstream_status}`           | The status code the origin returns. Several origins are separated by commas, and the groups of an internal redirect by a colon, as in `${upstream_addr}`                                                                                              | `200, 201`, or `500, 502 : 200, 200` after a redirect                                                                     | Response          |
| `${uri}`                       | The normalized, URL-decoded URI of the request. The value can change while the request is processed, for example after an internal redirect or when an index file answers. For the query string or URL-encoded characters, read `${request_uri}`      | `/path/my file.txt`                                                                                                       | Request, Response |

### Mutual Transport Layer Security (mTLS) variables

These variables hold the client certificate that a request presents over mTLS, where the client also authenticates with a certificate. A criterion reads them only in the Request Phase. For how a workload asks for and validates client certificates, refer to [mTLS](/en/documentation/platform/workloads/mtls/).

| Variable                     | What it holds                                                                                                       | Example                                                                   |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `${ssl_client_cert}`         | The client certificate in Privacy-Enhanced Mail (PEM) format. Deprecated: read `${ssl_client_escaped_cert}` instead | `-----BEGIN CERTIFICATE-----` `MIICnz...` `-----END CERTIFICATE-----`     |
| `${ssl_client_escaped_cert}` | The client certificate in PEM format, as a URL-encoded string                                                       | `-----BEGIN%20CERTIFICATE-----%0AMIICnz...%0A-----END%20CERTIFICATE-----` |
| `${ssl_client_fingerprint}`  | The Secure Hash Algorithm 1 (SHA-1) fingerprint of the client certificate                                           | `2fd4e1c67a2d28fced849`                                                   |
| `${ssl_client_i_dn}`         | The issuer DN string of the client certificate                                                                      | `/C=US/ST=California/L=San Francisco/O=Example CA/CN=issuer.com`          |
| `${ssl_client_s_dn}`         | The subject DN string of the client certificate                                                                     | `/C=US/ST=California/L=San Francisco/O=Example CA/CN=example.com`         |
| `${ssl_client_s_dn_parsed}`  | The subject CN extracted from the client certificate, as a string                                                   | `example.com`                                                             |
| `${ssl_client_serial}`       | The serial number of the client certificate                                                                         | `6C:0A:83:7E:92:3B:D6:C6:E3:56:50:E7`                                     |
| `${ssl_client_v_end}`        | The expiration date of the client certificate, in the format `YYYYMMDDHHmmSS`                                       | `20230115120000`                                                          |
| `${ssl_client_v_remain}`     | The number of days until the client certificate expires                                                             | `100`                                                                     |
| `${ssl_client_v_start}`      | The start date of the client certificate, in the format `YYYYMMDDHHmmSS`                                            | `20230115120000`                                                          |
| `${ssl_client_verify}`       | The result of the client certificate verification                                                                   | `SUCCESS`, `FAILED:reason`, or `NONE`                                     |

Most mTLS services expect to receive the client certificate itself. A Request Phase rule can send `${ssl_client_escaped_cert}` to the origin in the `X-Forward-Client-Cert` (XFCC) header with *Add Request Header*, and the origin then reads the certificate data from that header.

### Variables in behavior arguments

A behavior that takes an argument can read the variables of its phase. For example, a rule can write the device group or the geolocation of a request into a cookie or a header.

This Response Phase rule sets a cookie that records the host of the request:

|    | Variable  | Operator   | Argument   |
| -- | --------- | ---------- | ---------- |
| If | `${host}` | `is_equal` | `host.com` |

|      | Behavior              | Argument                    |
| ---- | --------------------- | --------------------------- |
| Then | *Add Response Cookie* | `cookie-host-value=${host}` |

When the rule matches, the response carries `Set-Cookie: cookie-host-value=host.com`.

Two more variables act as functions: each takes an argument, and both work only inside a behavior argument.

| Variable                        | What it returns                                                                     | Example                                                                                                                                         |
| ------------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `${cookie_time_offset(number)}` | The current date plus an offset of `number` seconds, for the expiration of a cookie | `cookie-name=cookie-value; Expires=${cookie_time_offset(3600)}` in [Add Cookie](#add-cookie) makes the cookie expire 1 hour after it is created |
| `${encode_base64(string)}`      | `string`, encoded in base64                                                         | `${encode_base64(http://www.yourdomain.com/)}` returns `aHR0cDovL3d3dy55b3VyZG9tYWluLmNvbS8=`                                                   |

### Operators

A criterion compares its variable with its argument through an operator, and the API sends the operator as one of these values in `operator`. The operators the Console offers can vary with the variable the criterion uses.

| Operator              | The criterion matches when                                                                                   | Argument           |
| --------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------ |
| `is_equal`            | The value is exactly the argument                                                                            | String             |
| `is_not_equal`        | The value is not the argument                                                                                | String             |
| `starts_with`         | The value starts with the argument                                                                           | String             |
| `does_not_start_with` | The value does not start with the argument                                                                   | String             |
| `matches`             | The value matches the regular expression in the argument                                                     | Regular expression |
| `does_not_match`      | The value does not match the regular expression in the argument                                              | Regular expression |
| `exists`              | The variable has a value. `${arg_search}` exists when the query string carries a `search` argument           | None               |
| `does_not_exist`      | The variable has no value. `${arg_search}` does not exist when the query string carries no `search` argument | None               |

### Conditionals

Criteria combine inside groups, and a rule holds 1 to 5 groups of 1 to 10 criteria each. In a group, the first criterion carries the conditional `if`, and each later criterion carries `and` or `or`. The Console joins criteria with **And** and **Or**.

Inside a group, `and` takes precedence over `or`. To set the precedence yourself, split the conditions into groups: the groups of a rule are joined by `and`, so a request matches the rule only when it matches every group. For example, a rule that needs `A or B`, and also `C`, puts `A or B` in one group and `C` in a second one.

In the API, `criteria` is a list of groups, and each group is a list of criterion objects, as in `[[{ … }]]`.

---

## Behaviors

Behaviors are what a rule does to a request, or to a response, that matches its criteria, and a rule carries from 1 to 10 of them. The Console labels the first behavior row **Then** and each later row **And**, and **+ Add Behavior** adds a row. Some behaviors cannot be added together, or only under some conditions, and the Console refuses those combinations.

In the API, each behavior is an object with a `type`. A behavior that takes an argument carries it in `attributes.value`, and *Capture Match Groups* carries three named attributes instead. The Console refuses some arguments that contain a space, with `Argument cannot contain spaces, use %20 instead`.

The two API columns give the `type` each phase accepts, and Requires names the Product the application must have enabled. When that Product is off, the Console adds *- Required Application Accelerator*, *- Required Image Processor*, or *- Required Function* to the label. A new application has Cache and Functions on, and Application Accelerator and Image Processor off.

| Behavior                            | API `type`, Request Phase | API `type`, Response Phase | Requires                              |
| ----------------------------------- | ------------------------- | -------------------------- | ------------------------------------- |
| Add Cookie                          | `add_request_cookie`      | `set_cookie`               | Application Accelerator               |
| Add Request Header                  | `add_request_header`      | `add_response_header`      | None                                  |
| Bypass Cache                        | `bypass_cache`            | Not available              | Application Accelerator               |
| Capture Match Groups                | `capture_match_groups`    | `capture_match_groups`     | Application Accelerator               |
| Deliver                             | `deliver`                 | `deliver`                  | None                                  |
| Deny (403 Forbidden)                | `deny`                    | Not available              | None                                  |
| Enable Gzip                         | `enable_gzip`             | `enable_gzip`              | None                                  |
| Enforce HLS cache                   | Added by Azion            | Not available              | Live Ingest                           |
| Filter Request Cookie               | `filter_request_cookie`   | `filter_response_cookie`   | Application Accelerator               |
| Filter Request Header               | `filter_request_header`   | `filter_response_header`   | None                                  |
| Finish Request Phase                | `finish_request_phase`    | Not available              | None                                  |
| Forward Cookies                     | `forward_cookies`         | Not available              | Application Accelerator               |
| No Content (204)                    | `no_content`              | Not available              | None                                  |
| Optimize Images                     | `optimize_images`         | Not available              | Image Processor                       |
| Redirect HTTP to HTTPS              | `redirect_http_to_https`  | Not available              | HTTPS on the workload                 |
| Redirect To (301 Moved Permanently) | `redirect_to_301`         | `redirect_to_301`          | None                                  |
| Redirect To (302 Found)             | `redirect_to_302`         | `redirect_to_302`          | None                                  |
| Rewrite Request                     | `rewrite_request`         | Not available              | Application Accelerator               |
| Run Function                        | `run_function`            | `run_function`             | Application Accelerator and Functions |
| Set Cache Policy                    | `set_cache_policy`        | Not available              | None                                  |
| Set Connector                       | `set_connector`           | Not available              | None                                  |

### Add Cookie

*Add Cookie* adds a cookie in the `Set-Cookie` header, in either phase, and requires Application Accelerator on the application. In a Response Phase rule, the behavior is *Add Response Cookie*. The argument takes the form `cookie-name=cookie-value`, and the value can be a variable, as in `cookie-name=${arg_cookie}`. The Console checks the argument and shows `This cookie is not valid` when it cannot read one.

In the Response Phase, the argument can carry [Set-Cookie](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) attributes after the value, each one after a semicolon (`;`):

- `Expires=date`, in the format `EEE, d MMM yyyy HH:mm:ss Z`
- `Domain=domain-value`
- `Path=path-value`
- `Max-age=number`, a TTL in seconds that takes precedence over `Expires`
- `SameSite=value; Secure`
- `HttpOnly`

A cookie with several attributes chains them, as in `cookie-name=cookie-value; Domain=domain-value; Path=path-value; SameSite=value`. An attribute value can be a variable too, as in `Path=${uri}; Domain=${host}`. In the API, the Request Phase `type` is `add_request_cookie` and the Response Phase `type` is `set_cookie`, with the argument in `attributes.value`.

### Add Request Header

*Add Request Header* adds a header to the request that Azion sends to the origin, in the Request Phase, and requires no Product. A Response Phase rule adds the header to the response sent to the user instead, with the API `type` `add_response_header`. The argument takes the form `Field: value`, and the Console refuses any other shape with `Header must follow the header-name: value format`.

The header name takes only letters (`a-z`, `A-Z`), numbers (`0-9`), hyphens, and underscores, and any other character makes the header invalid. The header value takes letters, numbers, and these characters:

```text
_ :;.,/"'?!(){}[]@<>=-+*#$&`|~^%
```

The argument holds up to 1,600 characters, and a longer one fails with `Argument too long`. This argument is a valid header:

```text
example-field: example-value!
```

In the API, the behavior is `add_request_header`, with the header in `attributes.value`, such as `"Accept: image/webp"`. A behavior of type `add_header` is refused with `400` and code `10039`. The headers `Host`, `Connection`, `Range`, `X-Forward-For`, and `Cdn-Loop` cannot be overwritten or filtered.

### Bypass Cache

*Bypass Cache* sends the requests the rule matches to the origin, and Azion does not cache the response. It runs in the Request Phase and requires Application Accelerator on the application. The behavior does not change the browser cache, which *Set Cache Policy* sets through a cache setting. In the API, it is `{ "type": "bypass_cache" }`, with no attributes.

Bypass Cache acts on Azion's cache and not on the [Tiered Cache](/en/documentation/platform/applications/cache/tiered-cache/) layer. An application whose cache settings have Tiered Cache on keeps caching objects in that layer for the minimum TTL. For how Bypass Cache differs from a cache TTL of 0, refer to [How Applications works](/en/documentation/platform/applications/how-it-works/). To bypass the cache for one path, refer to [Bypass the cache for a path](/en/documentation/guides/application-performance/cache-and-purge/cache-settings/#bypass-the-cache-for-a-path).

### Capture Match Groups

*Capture Match Groups* applies a regular expression to a request field and stores the groups it captures in a temporary array. It runs in either phase and requires Application Accelerator on the application. *Rewrite Request* reads the array to build a new path. The array is local, so only the rule that captures it can read it.

| Argument              | API attribute    | Values                      | Description                                                                                                                                                                                          |
| --------------------- | ---------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| *captured array name* | `captured_array` | String, 1 to 10 characters  | The name of the array that holds the captures. Outside that range, the Console shows `Captured array name must have at least 1 character.` or `Captured array name must have at most 10 characters.` |
| **Subject**           | `subject`        | String, 4 to 50 characters  | The request field to read, written as a variable such as `${uri}`                                                                                                                                    |
| **Regex**             | `regex`          | String, 1 to 255 characters | The regular expression. Each group to capture goes in parentheses                                                                                                                                    |

A capture is read as `%{name[index]}`. For example, with `capture` as the array name, `${uri}` as the subject, and `^(.*/)([^/]*)$` as the regular expression, a request for `/path/image.jpg` fills three entries:

- `%{capture[0]} = "/path/image.jpg"`
- `%{capture[1]} = "/path/"`
- `%{capture[2]} = "image.jpg"`

A group can also carry a name, in the `?<name>` notation, and its capture is then read by that name instead of an index. This regular expression names the two groups `path` and `filename`: `^(?<path>.*/)(?<filename>[^/]*)$`.

### Deliver

*Deliver* ends the processing of the request and delivers the content to the user, and the rules that follow do not run. It runs in either phase, requires no Product, and takes no argument. In the API, it is `{ "type": "deliver" }`.

### Deny (403 Forbidden)

*Deny (403 Forbidden)* answers the request with a `403 Forbidden` page and ends the processing of the request. It runs in the Request Phase, requires no Product, and takes no argument. In the API, it is `{ "type": "deny" }`.

### Enable Gzip

*Enable Gzip* compresses the content with gzip when the browser of the user supports it. It runs in either phase, requires no Product, and is `{ "type": "enable_gzip" }` in the API. To turn compression on for an application, refer to [Compress application responses with gzip](/en/documentation/guides/application-performance/delivery-optimization/gzip-compression/).

### Enforce HLS cache

*Enforce HLS cache* imposes the cache policy Azion defines for live HLS transmissions, and requires Live Ingest. Azion adds the behavior to a Request Phase rule every time you select a Live Ingest source. The behavior does two things: it bypasses the cache rules of the application, and it applies the live HLS policy.

| Object             | Cache time |
| ------------------ | ---------- |
| Playlists, `.m3u8` | 5 seconds  |
| Chunks, `.ts`      | 60 seconds |

To apply the policy to a stream, refer to [Enforce HLS cache for live streaming](/en/documentation/guides/media-and-streaming/streaming/enforce-hls-cache/).

### Filter Request Cookie

*Filter Request Cookie* removes a cookie from the request that Azion sends to the origin, and requires Application Accelerator on the application. A Response Phase rule removes a cookie from the response sent to the user instead, with the API `type` `filter_response_cookie`. The argument is `cookie-name` to remove the cookie, or `cookie-name=cookie-value` to remove only that value. In the API, the Request Phase `type` is `filter_request_cookie`.

### Filter Request Header

*Filter Request Header* removes a header from the request that Azion sends to the origin, and requires no Product. A Response Phase rule removes a header from the response sent to the user instead, with the API `type` `filter_response_header`. The argument is the header name, such as `Header-Name`. In the API, the Request Phase `type` is `filter_request_header`.

Five headers cannot be filtered or overwritten:

- `Host`
- `Connection`
- `Range`
- `X-Forward-For`
- `Cdn-Loop`

### Finish Request Phase

*Finish Request Phase* ends the Request Phase. The behaviors after it in the rule, and the rules after that rule, do not run. It runs in the Request Phase, requires no Product, and is `{ "type": "finish_request_phase" }` in the API.

### Forward Cookies

*Forward Cookies* makes Azion forward the `Set-Cookie` header that the origin returns to users, even when the response comes from cache. It runs in the Request Phase, requires Application Accelerator on the application, and is `{ "type": "forward_cookies" }` in the API. A cached response can then carry the `Set-Cookie` of another user's session. To keep sessions apart, refer to [Forward cookies from the origin to the user](/en/documentation/guides/application-performance/cache-and-purge/cache-settings/#forward-cookies-from-the-origin-to-the-user).

Cookies created with JavaScript are an alternative to the `Set-Cookie` response header: a script creates, reads, and expires them through the `document.cookie` property. A JavaScript cookie takes this format:

```javascript
document.cookie = "username=John Doe; expires=Thu, 18 Dec 2020 12:00:00 UTC; path=/";
```

Azion does not filter the `Cookie` request header by default, whatever the Forward Cookies configuration, so JavaScript cookies reach the origin. For more information, refer to [JavaScript Cookies](https://www.w3schools.com/js/js_cookies.asp).

### No Content (204)

*No Content (204)* answers with `204` instead of the status code the origin returns. It runs in the Request Phase, requires no Product, and is `{ "type": "no_content" }` in the API.

### Optimize Images

*Optimize Images* applies [Image Processor](/en/documentation/platform/applications/image-processor/settings/) to the requests the rule matches, and requires Image Processor on the application. It runs in the Request Phase. In the API, it is `{ "type": "optimize_images" }`: it takes no attributes, and a rule can carry it as its only behavior.

### Redirect HTTP to HTTPS

*Redirect HTTP to HTTPS* redirects a request made over HTTP to HTTPS, and does nothing to a request already made over HTTPS. It runs in the Request Phase and is `{ "type": "redirect_http_to_https" }` in the API. It requires HTTPS enabled in the protocol settings of the [workload](/en/documentation/platform/workloads/) that delivers the application.

### Redirect To

*Redirect To (301 Moved Permanently)* and *Redirect To (302 Found)* send the user to the URL or URI in the argument, with that status code. Use `301` when a path changes for good, and `302` when the change is temporary. Both behaviors end the processing of the request, run in either phase, and require no Product. In a Response Phase rule, they run only when the origin returns `404`.

The Console asks for the argument with `Redirect target is required`, and refuses a space in it with `Redirect target cannot contain spaces, use %20 instead`. In the API, the types are `redirect_to_301` and `redirect_to_302`, with the target in `attributes.value`. This behavior sends a reader of a FAQ to its English version:

| Behavior                  | Argument     |
| ------------------------- | ------------ |
| *Redirect To (302 Found)* | `/en-us/faq` |

### Rewrite Request

*Rewrite Request* changes the path of the resource that Azion requests from the origin. It runs in the Request Phase, requires Application Accelerator on the application, and is `rewrite_request` in the API, with the new path in `attributes.value`. The new path can combine a string, the variables of the Request Phase, and the captures of *Capture Match Groups*, written as `%{name[index]}`.

For example, two behaviors in one rule send a request for `/original/image.jpg` to the origin as `/new/image.jpg`:

| Behavior               | Argument                                                                          |
| ---------------------- | --------------------------------------------------------------------------------- |
| *Capture Match Groups* | *captured array name* `capture`, **Subject** `${uri}`, **Regex** `/original/(.*)` |
| *Rewrite Request*      | `/new/%{capture[1]}`                                                              |

### Run Function

*Run Function* runs a [function instance](/en/documentation/platform/applications/functions-instances/) of the application on what the rule matches. It runs in either phase and requires Application Accelerator and [Functions](/en/documentation/platform/functions/) on the application. The instances live in the **Functions Instances** tab of the application, and a new application has Functions on. For the Response Phase, the Console function instance form states `Only Lua functions can be used in the Response phase.`

In the API, `attributes.value` holds the ID of the function instance, not of the function, as in `{ "type": "run_function", "attributes": { "value": <function-instance-id> } }`. To create an instance, refer to [Instantiate a function on an application](/en/documentation/guides/application-development/getting-started/instantiate-functions/).

### Set Cache Policy

*Set Cache Policy* applies a [cache setting](/en/documentation/platform/applications/cache/cache-settings/) to the requests the rule matches, which is how a cache setting reaches a request. It runs in the Request Phase and requires no other Product. The cache setting comes first, in the **Cache Settings** tab of the application, and the behavior then names it in a second list.

The cache setting holds how long an object stays in cache. Its [Application Accelerator](/en/documentation/platform/applications/cache/cache-settings/#application-accelerator) section holds the variations that set the cache key. In the API, `attributes.value` holds the ID of the cache setting. The API returns that ID as an integer, even when the request sends it as a string. A cache setting that a rule applies cannot be deleted: the API answers `400` with code `21014`.

### Set Connector

*Set Connector* sends the requests the rule matches to a [connector](/en/documentation/platform/connectors/), which reaches the origin. In Azion Console, origins have been redesigned as connectors, and this behavior names one. It runs in the Request Phase and requires no Product, so a rule with `${uri}` and *Set Connector* works on an application without Application Accelerator. In the API, `attributes.value` holds the ID of the connector, as the API section shows.

---

## API

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

| Operation                   | Request Phase                             | Response Phase                              |
| --------------------------- | ----------------------------------------- | ------------------------------------------- |
| Create a rule               | `POST /request_rules`                     | `POST /response_rules`                      |
| List the rules of the phase | `GET /request_rules`                      | `GET /response_rules`                       |
| Retrieve a rule             | `GET /request_rules/{request_rule_id}`    | `GET /response_rules/{response_rule_id}`    |
| Replace a rule              | `PUT /request_rules/{request_rule_id}`    | `PUT /response_rules/{response_rule_id}`    |
| Update part of a rule       | `PATCH /request_rules/{request_rule_id}`  | `PATCH /response_rules/{response_rule_id}`  |
| Delete a rule               | `DELETE /request_rules/{request_rule_id}` | `DELETE /response_rules/{response_rule_id}` |
| Reorder the rules           | `PUT /request_rules/order`                | `PUT /response_rules/order`                 |

A create answers `202` with a `state` of `pending`, and a list answers `200`. The reorder call takes an `order` array that lists the rule IDs of the phase in their new order.

This call creates a Request Phase rule that sends every request of the application to a connector:

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

The response carries `202` and a `state` of `pending`. The excerpt keeps the rule as the platform stored it:

```text
{"state":"pending","data":{"id":<rule-id>,"name":"send-to-connector","active":true,"criteria":[[{"conditional":"if","variable":"${uri}","operator":"starts_with","argument":"/"}]],"behaviors":[{"type":"set_connector","attributes":{"value":<connector-id>}}],"description":"","order":0,…}}
```

The platform adds `description`, an empty string when the create sends none, and `order`, the position of the rule in its phase. The same body with `${request_uri}` in place of `${uri}` fails with code `25047` on an application without Application Accelerator.

---

## CLI

Azion CLI creates a rule with `azion create rules-engine`, which reads the rule from a JSON file in the shape the API takes. `--phase` names the phase, and it defaults to `request`.

This file applies a cache setting to every request whose path starts with `/static/`:

```json
{
  "name": "apply-static-assets-cache",
  "description": "Applies the static-assets cache setting to /static/",
  "active": true,
  "criteria": [[{ "variable": "${uri}", "operator": "starts_with", "conditional": "if", "argument": "/static/" }]],
  "behaviors": [{ "type": "set_cache_policy", "attributes": { "value": <cache-setting-id> } }]
}
```

Save it as `rule.json`, replace `<cache-setting-id>` with the ID of a cache setting of the application, and create the rule:

```bash
azion create rules-engine --application-id <application-id> --phase request --file rule.json
```

```text
Created Rules Engine with ID <rule-id>
```

The CLI refuses two older shapes of a rule file. A criterion with `input_value` instead of `argument` fails with `json: unknown field "input_value"`. A behavior written as `{"type":"set_cache_settings","cache_settings_id":"<id>"}` fails with `data failed to match schemas in oneOf(RequestPhaseBehaviorRequest)`: the behavior is `set_cache_policy`, with the ID in `attributes.value`.

---

## Errors

A rejected rule returns an `errors` array. Each entry carries a `code`, a `title`, a `detail`, the `status`, and a `source` pointer that names the field, and some entries add a `meta` object. This body answers a rule with `${request_uri}` on an application without Application Accelerator:

```json
{
  "errors": [
    {
      "code": "25047",
      "title": "Missing Required Modules",
      "detail": " It requires any of the following modules to be enabled: ['application_accelerator'].",
      "status": "400",
      "source": { "pointer": "/data/criteria/0/0/variable" },
      "meta": {
        "message_prefix": "",
        "owner_modules": "any",
        "missing_required_modules": ["application_accelerator"]
      }
    }
  ]
}
```

| Code    | Title                       | Status | What causes it                                                                                                                                                                                                                        | What to do                                                                                                                                                                   |
| ------- | --------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `10039` | Invalid Choice              | 400    | A behavior `type` that is not a valid choice, such as `add_header`, with the detail `"add_header" is not a valid choice.` The `source` pointer names the behavior, as in `/data/behaviors/0/type`, and `meta.input` repeats the value | Send a `type` from the behavior table, such as `add_request_header`                                                                                                          |
| `21014` | Cannot Delete Cache Setting | 400    | Deleting a cache setting that a rule applies with `set_cache_policy`                                                                                                                                                                  | Remove the behavior that names the cache setting, or delete the rule, then delete the cache setting                                                                          |
| `25047` | Missing Required Modules    | 400    | A criterion variable whose Product is off on the application, such as `${request_uri}` without Application Accelerator. `meta.missing_required_modules` names the Product                                                             | Enable Application Accelerator in the **Modules** section of the application's [Main Settings](/en/documentation/platform/applications/main-settings/), or match on `${uri}` |

---

## Related resources

- [How Applications works](/en/documentation/platform/applications/how-it-works.md): How an application runs its rules in each phase, in which order, and when a behavior stops the rest.
- [Create request and response rules](/en/documentation/guides/application-development/getting-started/rules-engine.md): The procedure that builds a rule in Azion Console.
- [Configure cache policies for an application](/en/documentation/guides/application-performance/cache-and-purge/cache-settings.md): The procedures that apply a cache setting, bypass the cache for a path, and forward cookies from the origin.
- [Debug rules created with Rules Engine](/en/documentation/guides/application-development/getting-started/debug-rules.md): Debug the rules of an application through the GraphQL API, Data Stream, or Real-Time Events.
