---
title: 'Tutorial: Configure Deny Rules with Domain Routing Using the Access Server CLI'
canonical: https://openvpn.net/as-docs/v3/tutorials/tutorial--deny-rules-with-domain-routing/
product: Access Server Docs
description: Configure deny rules with Domain Routing in OpenVPN Access Server using CLI. Learn how to block specific domains, combine allow and deny rules, and verify behavior using DNS proxy logs.
---

# Tutorial: Configure Deny Rules with Domain Routing Using the Access Server CLI

## Overview

By default, Access Server supports allowlist rules for domain routing. This tutorial explains how to configure and validate deny rules using the CLI.

You can apply domain routing rules at different levels:

- Globally (applies to all users).
- Per Group.
- Per individual user.

You can use one or more levels at the same time, depending on your access requirements.

Domain routing supports:

- **Exact domains** (for example, `example.com`) — matches only that domain.
- **Wildcard domains** (for example, `*.example.com`) — matches the domain and all subdomains.
- **Wildcard TLDs** (for example, `*.com`) — matches all domains with that top-level domain (TLD).

Deny rules let you block traffic to specific domains, subdomains, or TLDs.

## Prerequisites

- Access Server 3.2.0 or newer.
- Root privileges on your Access Server's console.
- DNS server proxy enabled globally.
- One or more users or groups configured.

> **Note**
>
> In our documentation, we use example IPv4 addresses and subnets [reserved for documentation](https://en.wikipedia.org/wiki/Reserved_IP_addresses#IPv4), such as `192.0.2.0/24`, `198.51.100.0/24`, and `203.0.113.0/24`.
>
> Ensure you replace them with valid IPv4 addresses and subnets for your network(s).

> **Important**
>
> Before you begin, take note of the following:
>
> - Deny rules for domains are supported starting in Access Server 3.2.0.
> - You need to configure deny rules using the CLI.
> - Rule order matters. Deny rules need to come before allow rules when both apply.

## Scenario 1: Deny access to a specific domain

In this scenario, you block access to `app1.example.com` for users connected to the VPN.

> **Tip**
>
> This scenario applies to both full and split tunneling.

### Step 1: Create a ruleset and deny rule

1. Connect to the console and get root privileges.
2. Create a global ruleset:

   ```
   sacli --name "__DEFAULT___default_ruleset" --comment "Default ruleset for global domain rules" AccessControlRulesetsAdd
   ```

   - Example output:

     ```
     1
     ```
3. Note this as the returned ruleset ID.
4. Create a deny rule for `app1.example.com`:

   ```
   sacli --ruleset_id "1" --rule_type "domain_routing" --match_type "domain" --match_data "app1.example.com" --action "deny" --position "1" --comment "" AccessControlRulesAdd
   ```

   | **Parameter** | **Value** |
   | --- | --- |
   | `ruleset_id` | Use the value from the global ruleset output. |
   | `match_type` with `domain` | Match an exact domain. For example, `app1.example.com`. |
   | `match_type` with `domain_or_subdomain` | Match wildcard domains. For example, `example.com`. > **Tip** > > Wildcard domain syntax differs between interfaces: > > - **CLI:** `example.com` > - **Admin Web UI:** `*.example.com` |
   | `match_data` | Domain to route. |
   | `action` with `deny` | This will block the traffic to the domain. |
   | `position` | Use the value for the next available position noted above. |
5. Attach the ruleset globally:

   ```
   sacli --user "__DEFAULT__" --position "0" --ruleset_id "1" AccessControlUserRulesetsAdd
   ```

   > **Tip**
   >
   > Global rules use `__DEFAULT__`. Replace this value when applying rules to specific users or groups.
6. Apply the changes:

   ```
   sacli start
   ```

### Step 2: Verify the rule configuration

1. List rulesets:

   ```
   sacli AccessControlRulesetsList
   ```

   - Example output:

     ```
     [
       {
         "comment": "Default ruleset for global domain rules",
         "id": 1,
         "name": "__DEFAULT___default_ruleset",
         "owner": "__DEFAULT__",
         "owner_type": "user_default",
         "position": 0
       }
     ]
     ```
2. Note the ruleset ID.
3. List rules in the ruleset:

   ```
   sacli --ruleset_id '1' AccessControlRulesList
   ```

   Replace the `ruleset_id` value with the value from the ruleset list output.

   - Example output:

     ```
     [
       {
         "action": "deny",
         "comment": "",
         "id": 1,
         "match_data": "app1.example.com",
         "match_type": "domain",
         "position": 0,
         "ruleset_id": 1,
         "type": "domain_routing"
       }
     ]
     ```
4. Confirm the rule includes your deny list values. Example:
   - `action: deny`
   - `match_data: app1.example.com`

### Step 3: Verify traffic is denied

In this step, connect to the VPN as a user and generate traffic to `app1.example.com` to verify it's denied.

1. Connect a VPN client.
2. From the Access Server console, enable DNS proxy debug logging:

   ```
   echo "DEBUG_DNSPROXY=true" >> /usr/local/openvpn_as/etc/as.conf && systemctl restart openvpnas
   ```
3. From the client, run:

   ```
   ping app1.example.com
   ```

   - Example output:

     ```
     Ping request could not find host app1.example.com. Please check the name and try again.
     ```
4. On the server, run:

   ```
   grep "DNSPROXY" /var/log/openvpnas.log
   ```

   - Example output:

     ```
     2026-04-16T21:05:11+0000 [stdout#info] [DNSPROXY] OUT: '[335] Received query from 172.27.232.2:53380'
     2026-04-16T21:05:11+0000 [stdout#info] [DNSPROXY] OUT: '[335] Deny query for app1.example.com. IN A: domain is blocked'
     ```

   > **Tip**
   >
   > Debug flags can generate large amounts of log data. After collecting the necessary information, remove the debug flag and restart Access Server to prevent excessive logging.

## Scenario 2: Deny a specific domain and allow a wildcard

In this scenario:

- Deny: `app1.example.com`
- Allow: `*.example.com`

> **Tip**
>
> This scenario applies to both full and split tunneling.

### Step 1: Create a ruleset and rules

1. Connect to the console and get root privileges.
2. Create a global ruleset:

   ```
   sacli --name "__DEFAULT___default_ruleset" --comment "Default ruleset for global domain rules" AccessControlRulesetsAdd
   ```

   - Example output:

     ```
     1
     ```
3. Note this as the returned ruleset ID.
4. Add rules to deny the specific domain and allow a wildcard domain:

   ```
   sacli --ruleset_id "1" --rule_type "domain_routing" --match_type "domain" --match_data "app1.example.com" --action "deny" --position "1" --comment "" AccessControlRulesAdd
   sacli --ruleset_id "1" --rule_type "domain_routing" --match_type "domain_or_subdomain" --match_data "example.com" --action "nat" --position "2" --comment "" AccessControlRulesAdd
   ```

   | **Parameter** | **Value** |
   | --- | --- |
   | `ruleset_id` | Use the value from the global ruleset output. |
   | `match_type` with `domain` | Match an exact domain. For example, `app1.example.com`. |
   | `match_type` with `domain_or_subdomain` | Match wildcard domains. For example, `example.com`. > **Tip** > > Wildcard domain syntax differs between interfaces: > > - **CLI:** `example.com` > - **Admin Web UI:** `*.example.com` |
   | `match_data` | Domain to route. |
   | `action` with `deny` | This will block the traffic to the domain. |
   | `position` | Use the value for the next available position noted above. |
5. Attach the ruleset:

   ```
   sacli --user "__DEFAULT__" --position "0" --ruleset_id "1" AccessControlUserRulesetsAdd
   ```

   > **Tip**
   >
   > Global rules use `__DEFAULT__`. Replace this value when applying rules to specific users or groups.
6. Apply changes:

   ```
   sacli start
   ```

### Step 2: Verify rule configuration

1. List rulesets:

   ```
   sacli AccessControlRulesetsList
   ```

   - Example output:

     ```
     [
       {
         "comment": "Default ruleset for global domain rules",
         "id": 1,
         "name": "__DEFAULT___default_ruleset",
         "owner": "__DEFAULT__",
         "owner_type": "user_default",
         "position": 0
       }
     ]
     ```
2. Note the ruleset ID.
3. List rules in the ruleset:

   ```
   sacli --ruleset_id '1' AccessControlRulesList
   ```

   Replace the `ruleset_id` value with the value from the ruleset list output.

   - Example output:

     ```
     [
       {
         "action": "deny",
         "comment": "",
         "id": 1,
         "match_data": "app1.example.com",
         "match_type": "domain",
         "position": 1,
         "ruleset_id": 1,
         "type": "domain_routing"
       },
       {
         "action": "nat",
         "comment": "",
         "id": 2,
         "match_data": "example.com",
         "match_type": "domain_or_subdomain",
         "position": 2,
         "ruleset_id": 1,
         "type": "domain_routing"
       }
     ]
     ```
4. Confirm your rules are correct. From our example:
   - The deny rule is first.
   - The allow rule follows.

### Step 3: Verify traffic is denied

In this step, connect to the VPN as a user and generate traffic to `app1.example.com` to verify it's denied, while traffic to the rest of `*.example.com` is allowed.

1. Connect a VPN client.
2. From the Access Server console, enable DNS proxy debug logging:

   ```
   echo "DEBUG_DNSPROXY=true" >> /usr/local/openvpn_as/etc/as.conf && systemctl restart openvpnas
   ```
3. From the client, run:

   ```
   ping app1.example.com
   ```

   - Example output:

     ```
     Ping request could not find host app1.example.com. Please check the name and try again.
     ```
4. Test the allowed domain:

   ```
   ping app2.example.com
   ```

   - Example output:

     ```
     Reply from 100.64.0.1: bytes=32 time=116ms TTL=49
     Reply from 100.64.0.1: bytes=32 time=116ms TTL=49
     Reply from 100.64.0.1: bytes=32 time=114ms TTL=49
     Reply from 100.64.0.1: bytes=32 time=115ms TTL=49
     ```
5. On the server, run:

   ```
   grep "DNSPROXY" /var/log/openvpnas.log
   ```

   - Example output:

     ```
     2026-04-16T21:16:41+0000 [stdout#info] [DNSPROXY] OUT: '[867] Received query from 172.27.236.2:60873'
     2026-04-16T21:16:41+0000 [stdout#info] [DNSPROXY] OUT: '[867] Forwarding query for app2.example.com. IN A'
     2026-04-16T21:16:41+0000 [stdout#info] [DNSPROXY] OUT: '[867] Received response'
     2026-04-16T21:16:41+0000 [stdout#info] [DNSPROXY] OUT: '[867] Changed address for app2.example.com: 192.0.2.1 -&gt; 100.64.0.1'
     ```

   > **Tip**
   >
   > Debug flags can generate large amounts of log data. After collecting the necessary information, remove the debug flag and restart Access Server to prevent excessive logging.

---

Prev: [Tutorial: Configure Domain Routing Settings Using the Access Server CLI](https://openvpn.net/as-docs/v3/tutorials/tutorial--domain-routing-settings-via-cli.md)
Next: [Tutorial: Configure Domain Routing Exclusion and Bypass Rules Using the Access Server CLI](https://openvpn.net/as-docs/v3/tutorials/tutorial--domain-routing-exclusion-and-bypass-rules-via-cli.md)
Index: https://openvpn.net/as-docs/llms.txt
