---
title: 'Tutorial: Configure Domain Routing Rules in Access Server from the Command-Line Interface'
canonical: https://openvpn.net/as-docs/v3/tutorials/tutorial--domain-routing-via-cli/
product: Access Server Docs
description: Need to route specific domains through OpenVPN Access Server? This tutorial shows how to configure domain routing using the CLI with practical command examples.
---

# Tutorial: Configure Domain Routing Rules in Access Server from the Command-Line Interface

## Overview

Domain routing lets you control whether traffic to specific domains is sent through the VPN tunnel instead of routed directly to the internet. This is useful when IP-based routing is unreliable due to dynamic IPs or CDN-backed services.

You can configure domain routing rules:

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

You can use one, some, or all of these levels at the same time, depending on your access requirements.

Domain routing rules support:

- Exact domains (for example, `example.com`): Traffic is routed through the VPN only when the destination matches the exact domain. Subdomains (such as `app.example.com`) aren't included.
- Wildcard domains (for example, `*.example.com`): Traffic is routed through the VPN for the specified domain and all of its subdomains.
- Wildcard TLDs (for example, `*.com`): Traffic is routed through the VPN for all the domains that use this top-level domain (TLD).

## Prerequisites

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

> **Important**
>
> Before you begin, be aware of the following behavior:
>
> - Traffic for domains not explicitly defined in domain routing rules behaves as follows:
>   - When using **full tunnel** routing, traffic to other domains is still routed through the VPN if their resolved IP addresses are public IPs.
>   - When using **split tunnel** routing, only traffic to domains explicitly defined in access rules is routed through the VPN. All other traffic is routed directly to your local internet.
> - When a domain is specified as the destination for a user or group rule:
>   - **Protocol** and **port** options are disabled.
> - Domain-based access rules require **DNS server proxy**. This can only be disabled using **split tunnel**. If DNS server proxy is disabled, the rules remain visible but are marked as inactive with a warning icon.

## Step 1: Determine whether a ruleset already exists

Before creating a domain routing rule from the CLI, determine whether a ruleset already exists for the scope you want to modify.

Rulesets may already exist if you previously created domain routing rules in the Admin Web UI.

1. Check for existing rulesets:

   ```
   sacli AccessControlRulesetsList
   ```
2. Look for a ruleset where the **owner** matches what you want to configure:

   | **Scope** | **Owner value** |
   | --- | --- |
   | Global rules | `__DEFAULT__` |
   | User rules | the username (example: `brandon`) |
   | Group rules | the group name (example: `support`) |
3. If you find a matching ruleset, follow **Option A**.
4. If no matching ruleset exists, follow **Option B**.

## Step 2: Add a rule to an existing ruleset (Option A)

If domain routing rules were [previously created in the Admin Web UI](https://openvpn.net/as-docs/v3/tutorials/tutorial--domain-routing-in-access-server.md), a **ruleset already exists**. You must add new rules to that ruleset.

1. To identify the ruleset ID, run:

   ```
   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 **id** value (in this example: `1`).
3. To check for existing rules in the ruleset, run:

   ```
   sacli --ruleset_id 'id' AccessControlRulesList
   ```

   Replace `ruleset_id` with the **id** value noted above.

   - Example output:

     ```
     [
       {
         "action": "nat",
         "comment": "",
         "id": 1,
         "match_data": "app1.example.com",
         "match_type": "domain",
         "position": 0,
         "ruleset_id": 1,
         "type": "domain_routing"
       }
     ]
     ```
4. Note the **position** of the last rule.
5. New rules must use the **next available position**.
   - Example:

     ```
     existing rule position = 0
     next rule position = 1
     ```
6. Create a new, global domain rule:

   ```
   sacli --ruleset_id "1" --rule_type "domain_routing" --match_type "domain" --match_data "app2.example.com" --action "nat" --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, `app2.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 `nat` or `route` | How to manage traffic for this domain. NAT: traffic leaves using the server IP. Route: traffic keeps the client VPN IP. |
   | `position` | Use the value for the next available position noted above. |
7. Restart Access Server:

   ```
   sacli start
   ```
8. Verify the rule:

   ```
   sacli --ruleset_id 'id' AccessControlRulesList
   ```

   Replace `ruleset_id` with the **id** noted above.

## Step 3: Create a new ruleset and rule (Option B)

If **no ruleset exists**, you must create one before adding rules.

1. Create a new global ruleset:

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

   - Example output:

     ```
     1
     ```
2. Take note of this number as the **ruleset id**.
3. Create your new global domain routing rule:

   ```
   sacli --ruleset_id "1" --rule_type "domain_routing" --match_type "domain" --match_data "app2.example.com" --action "nat" --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, `app2.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 `nat` or `route` | How to manage traffic for this domain. NAT: traffic leaves using the server IP. Route: traffic keeps the client VPN IP. |
   | `position` | Use the value for the next available position noted above. |
4. Attach the ruleset to the target:

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

   > **Tip**
   >
   > Global rules use the `__DEFAULT__` user. For users or groups, replace `__DEFAULT__` with the username or group name.
5. Restart Access Server:

   ```
   sacli start
   ```
6. Verify the rule:

   ```
   sacli --ruleset_id 'id' AccessControlRulesList
   ```

   Replace `ruleset_id` with the **id** noted above.

## Step 4: Edit a domain routing rule

In this step, we assume the following user ruleset and rule.

- Example ruleset:

  ```
  [
      "comment": "Auto-created ruleset for user brandonqa",
      "id": 2,
      "name": "Ruleset for brandonqa",
      "owner": "brandonqa",
      "owner_type": "user_connect",
      "position": 1
  ]
  ```
- Example rule:

  ```
  [
    {
      "action": "nat",
      "comment": "",
      "id": 1,
      "match_data": "app3.example.com",
      "match_type": "domain",
      "position": 1,
      "ruleset_id": 2,
      "type": "domain_routing"
    }
  ]
  ```

For our example, we will update the rule to change the domain from `app3.example.com` to `app4.example.com`.

1. Run the following command to update the rule:

   ```
   sacli --rule_id '1' --ruleset_id '2' --rule_type 'domain_routing' --match_type 'domain' --match_data 'app4.example.com' --action 'nat' --position '1' --comment '' AccessControlRulesUpdate
   ```

   | **Parameter** | **Value** |
   | --- | --- |
   | `ruleset_id` | Use the value from the global ruleset output. |
   | `match_type` with `domain` | Match an exact domain. For example, `app2.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 `nat` or `route` | How to manage traffic for this domain. NAT: traffic leaves using the server IP. Route: traffic keeps the client VPN IP. |
   | `position` | Use the value for the next available position noted above. |

   > **Tip**
   >
   > In our example, we modify only the domain, but you can modify any value based on your needs.
2. Restart services:

   ```
   sacli start
   ```
3. Verify the rule:

   ```
   sacli --ruleset_id 'id' AccessControlRulesList
   ```

   Replace `ruleset_id` with the **id** noted above.

## Step 5: Remove a domain routing rule

In this step, we assume the following user ruleset and rule.

- Example ruleset:

  ```
  [
      "comment": "Auto-created ruleset for user brandonqa",
      "id": 2,
      "name": "Ruleset for brandonqa",
      "owner": "brandonqa",
      "owner_type": "user_connect",
      "position": 1
  ]
  ```
- Example rule:

  ```
  [
    {
      "action": "nat",
      "comment": "",
      "id": 1,
      "match_data": "app5.example.com",
      "match_type": "domain",
      "position": 1,
      "ruleset_id": 2,
      "type": "domain_routing"
    }
  ]
  ```

1. Run the following command to delete the rule:

   ```
   sacli --rule_id '1' AccessControlRulesDelete
   ```

   Replace `rule_id` with the **id** from the rule you're deleting.
2. Restart services:

   ```
   sacli start
   ```
3. Verify the rule was deleted:

   ```
   sacli --ruleset_id 'id' AccessControlRulesList
   ```

   Replace `ruleset_id` with the **id** noted above.

---

Prev: [Tutorial: Step-by-Step Examples for Split Tunnel and Role-Based Access Using Domain Routing](https://openvpn.net/as-docs/v3/tutorials/tutorial--domain-routing-split-tunnel-and-role-based-access.md)
Next: [Tutorial: Configure Domain Routing Settings Using the Access Server CLI](https://openvpn.net/as-docs/v3/tutorials/tutorial--domain-routing-settings-via-cli.md)
Index: https://openvpn.net/as-docs/llms.txt
