# Firewall quickstart

This guide instructs you through denying your first request with a [firewall](/en/documentation/platform/firewall/).

- Create a firewall for the application you already serve.
- Add a rule that denies every request to one path.
- Bind the firewall to the [workload](/en/documentation/platform/workloads/) that serves your application.
- Send a request to that path and read the `403` it returns.

Four objects turn a request into a decision, and each one links to the next:

1. The **firewall** holds the rules. It is active from the moment you create it.
2. The **rule** on that firewall pairs a criterion with a behavior. The criterion selects requests, and the behavior decides what happens to them. In this guide, the criterion selects every path that starts with `/deny-test`, and the behavior denies the request.
3. The **deployment** of the workload that serves your application names that firewall, beside the application.
4. The **request** to the workload's domain meets the rule, and a denied request never reaches your application.

A firewall inspects nothing on its own. Until a workload's deployment names it, no request reaches its rules.

Web Application Firewall (WAF), Network Shield, and Bot Manager are Products enabled on a firewall. This guide needs none of them, because Deny is built into every firewall. Each Product's quickstart starts from a firewall bound to a workload, which is what this guide leaves you with.

---

Select the interface you use. The prerequisites and every stage on this page follow that choice.

## Prerequisites

- An Azion account. To create one, refer to [Create an account](/en/documentation/fundamentals/creating-account/).
- A workload that serves an application on its workload domain. For more information, refer to [Workloads](/en/documentation/platform/workloads/).

**Console**

- Access to Azion Console. To sign in, refer to [Access Azion Console](/en/documentation/guides/platform/account-and-billing/how-to-access-azion-console/).

**CLI**

- The [Azion CLI](/en/documentation/devtools/cli/), installed and authorized.
- The ID of your workload and the ID of the application it serves.

**API**

- A personal token and `curl`. To create a token, refer to [Manage a personal token](/en/documentation/guides/platform/account-and-billing/personal-tokens/).
- The ID of your workload and the ID of the application it serves.

---

## Create the firewall

A new firewall holds no rules, and it starts active. The Deny rule this guide adds needs no Product turned on, so the default settings stay.

**Console**

To create the firewall in Azion Console:

1. **Open the Firewalls page**

   Access [Azion Console](https://console.azion.com/) > **Secure** > **Firewalls**.

2. **Start a new firewall**

   Select **Firewall**. The **Create Firewall** page opens.

3. **Name the firewall**

   In the **General** section, enter a **Name**. For example: `storefront-firewall`.

4. **Leave the other sections as they open**

   A Deny rule needs no switch in **Modules**. In **Status**, **Active** stays turned on.

5. **Select Create**

Azion Console shows the message "Your Firewall has been created" and opens the firewall. Its tabs are **Main Settings**, **Functions Instances**, and **Rules Engine**. **Functions Instances** appears only while **Functions** is turned on.

**CLI**

To create the firewall with the Azion CLI:

```bash
azion create firewall --name "storefront-firewall" --active true
```

The command prints the ID of the new firewall:

```text
Created Firewall with ID 12352
```

The firewall is active, with Functions and Network Shield turned on and WAF turned off. Record the ID. The rule and the binding to your workload pass it as `--firewall-id`.

**API**

To create the firewall with the API, send a `POST` request to the firewalls endpoint. Replace `[TOKEN VALUE]` with your personal token:

```bash
curl --request POST \
  --url https://api.azion.com/v4/workspace/firewalls \
  --header 'Accept: application/json' \
  --header 'Authorization: Token [TOKEN VALUE]' \
  --header 'Content-Type: application/json' \
  --data '{"name": "storefront-firewall"}'
```

This excerpt of the response shows the new firewall and the settings it starts with:

```json
{
  "state": "pending",
  "data": {
    "id": 12353,
    "name": "storefront-firewall",
    "modules": {
      "ddos_protection": { "enabled": true },
      "functions": { "enabled": true },
      "network_protection": { "enabled": true },
      "waf": { "enabled": false }
    },
    "debug": false,
    "active": true,
    …
    "product_version": "2.0",
    …
    "version": 0
  }
}
```

The firewall is active, and `waf` is the one entry in `modules` that starts turned off. Record the `id`. The rule and the binding to your workload pass it as `<firewall-id>`.

---

## Add a rule that denies one path

A [Rules Engine for Firewall](/en/documentation/platform/firewall/rules-engine/) rule pairs criteria with behaviors. The rule in this stage carries one criterion, a request URI that starts with `/deny-test`, and one behavior, Deny. Deny refuses the request with `403 Forbidden`, and it needs no Product turned on.

The criterion matches every path that begins with `/deny-test`, so `/deny-test/page` matches too. For more information, refer to [Deny (403 Forbidden)](/en/documentation/platform/firewall/rules-engine/#deny-403-forbidden).

**Console**

To add the rule in Azion Console:

1. **Open the firewall**

   Access [Azion Console](https://console.azion.com/) > **Secure** > **Firewalls**, and select the firewall you created.

2. **Select the Rules Engine tab**

3. **Start a new rule**

   Select **Rule**. The **Create Rule** drawer opens.

4. **Name the rule**

   In the **General** section, enter a **Name**. For example: `Deny the test path`.

5. **Set the criterion**

   In the **Criteria** section, the first criterion opens on *Request Uri* and *starts with*. Enter `/deny-test` as its argument.

6. **Select the Deny behavior**

   In the **Behaviors** section, select *Deny (403 Forbidden)*.

7. **Select Save**

Azion Console shows the message "Rule successfully created". The rule appears in the **Rules Engine** list with the status *Active*.

**CLI**

`azion create firewall-rule` reads the whole rule from a JSON file. `criteria` is a list of groups, and the first criterion of a group carries the conditional `if`. Save this rule as `rule.json`:

```json
{
  "name": "Deny the test path",
  "active": true,
  "criteria": [
    [
      {
        "variable": "${request_uri}",
        "conditional": "if",
        "operator": "starts_with",
        "argument": "/deny-test"
      }
    ]
  ],
  "behaviors": [
    { "type": "deny" }
  ]
}
```

Create the rule. Replace `<firewall-id>` with the ID of your firewall:

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

The command prints the ID of the new rule:

```text
Created Firewall Rule with ID 123460
```

The rule is active on the firewall.

**API**

The request body is the rule itself. `criteria` is a list of groups, and the first criterion of a group carries the conditional `if`. Save this rule as `rule.json`:

```json
{
  "name": "Deny the test path",
  "active": true,
  "criteria": [
    [
      {
        "variable": "${request_uri}",
        "conditional": "if",
        "operator": "starts_with",
        "argument": "/deny-test"
      }
    ]
  ],
  "behaviors": [
    { "type": "deny" }
  ]
}
```

Send a `POST` request to the rules of your firewall. Replace `<firewall-id>` with the ID of your firewall:

```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 @rule.json
```

This excerpt of the response shows the new rule's `id` and its `order` among the rules of the firewall:

```json
{
  "state": "pending",
  "data": {
    "id": 123459,
    …
    "behaviors": [{ "type": "deny" }],
    "description": "",
    "order": 0
  }
}
```

The API assigns `order` in creation order, starting at `0`. The rule is active on the firewall.

---

## Bind the firewall to your workload

A firewall has no domain of its own. The workload carries the domain. Its deployment holds exactly three settings: the application, the firewall, and the custom page. Name the application the workload serves today, and its custom page if it uses one, beside your firewall. Until the deployment names your firewall, the Deny rule matches no request.

**Console**

To bind the firewall in Azion Console:

1. **Open the Workloads page**

   Access [Azion Console](https://console.azion.com/) > **Workloads**.

2. **Open your workload**

   Select the workload that serves your application. Its edit form opens.

3. **Select the firewall**

   In the **Firewall** field of **Deployment Settings**, select the firewall you created. **Application** and **Custom Page** keep what the workload serves today.

4. **Select Save**

The deployment of the workload names your firewall, beside its application and its custom page.

**CLI**

To bind the firewall with the Azion CLI, create a deployment that names your firewall and the application the workload serves today. Replace the three IDs:

```bash
azion create workload-deployment --workload-id <workload-id> \
  --name storefront-deployment --application-id <application-id> \
  --firewall-id <firewall-id> --strategy-type default --active true --current true
```

The command prints the ID of the new deployment:

```text
Created Workload Deployment with ID 123462
```

`--current true` makes it the current deployment of the workload, which names your application and your firewall. If the workload uses a custom page, add `--custom-page <custom-page-id>` to the command.

**API**

To bind the firewall with the API, send a `POST` request to the deployments of your workload. The firewall goes in `strategy.attributes.firewall`, beside the application the workload serves today. If the workload uses a custom page, name it in `custom_page` too. Replace the three IDs:

```bash
curl --request POST \
  --url https://api.azion.com/v4/workspace/workloads/<workload-id>/deployments \
  --header 'Accept: application/json' \
  --header 'Authorization: Token [TOKEN VALUE]' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "storefront-deployment",
  "current": true,
  "active": true,
  "strategy": {
    "type": "default",
    "attributes": {
      "application": <application-id>,
      "firewall": <firewall-id>
    }
  }
}'
```

The response returns the deployment. Its `strategy.attributes` holds the application, the firewall, and `custom_page`, which is `null` when the workload uses no custom page:

```json
{
  "state": "pending",
  "data": {
    "id": 123461,
    "name": "storefront-deployment",
    "current": true,
    "active": true,
    "strategy": {
      "type": "default",
      "attributes": {
        "application": <application-id>,
        "firewall": <firewall-id>,
        "custom_page": null
      }
    },
    "last_editor": "user@example.com",
    "last_modified": "2026-01-01T12:00:00.000000Z",
    "created_at": "2026-01-01T12:00:00.000000Z"
  }
}
```

The current deployment of the workload names your application and your firewall.

---

## Confirm that the path answers 403

The check is the same whichever interface built the firewall. Your workload answers on its workload domain, of the form `<id>.map.azionedge.net`, written in this stage as `<your-workload-domain>`.

A workload newly bound to a firewall can take several minutes to enforce its first rule, and no duration is guaranteed. While the binding spreads, answers to the same request alternate between the earlier response and the new one. Until then, the path still reaches your application. Send the request again until it answers `403`. For more information, refer to [Propagation](/en/documentation/platform/firewall/how-it-works/#propagation).

Send a request to the denied path:

```bash
curl -i https://<your-workload-domain>/deny-test
```

The firewall refuses it with `403`:

```text
HTTP/2 403
server: nginx
date: Thu, 01 Jan 2026 12:00:00 GMT
content-type: text/html; charset=utf-8
x-content-type-options: nosniff
x-azion-request-id: 0123456789abcdef0123456789abcdef
x-azion-edge-location: <edge-location>
alt-svc: h3=":443"; ma=86400
```

The response also carries a long `content-security-policy` header, omitted from this excerpt. The body is Azion's default error page, headed Forbidden, and it lists your IP address and the request ID. No header names the firewall or the rule that refused the request.

Requests to every other path do not match the rule, and your application answers them as before. Your firewall denies every request whose path starts with `/deny-test`.

---

## Next steps

- [WAF quickstart](/en/documentation/platform/firewall/waf/quickstart.md): Create a WAF rule set, apply it with a Set WAF behavior, and see an injection-shaped request refused.
- [Network Shield quickstart](/en/documentation/platform/firewall/network-shield/quickstart.md): Put your own IP address in a network list and deny that list on one test path.
- [Bot Manager quickstart](/en/documentation/platform/firewall/bot-manager/quickstart.md): Run Bot Manager Lite from a Run Function rule and read the score it gives a request.
- [Rules Engine for Firewall](/en/documentation/platform/firewall/rules-engine.md): Every criterion, operator, and behavior a firewall rule accepts, with the rule body for the API and the CLI.
- [How Firewall works](/en/documentation/platform/firewall/how-it-works.md): The path a request travels through a firewall, the order its rules run in, and how a change propagates.
- [Firewall limits](/en/documentation/platform/firewall/limits.md): The bounds on rules and function instances per firewall, and the limits each Product adds.
