Files
panel/docs/learn-en/server-routing.md
Nick 73925574f2 Removed email references (#282)
* Removed email references

* fix должна
2025-12-25 15:24:23 +03:00

11 KiB

title, sidebar_position
title sidebar_position
Server-Side Routing 7

:::warning Demonstration Only | Not Production-Ready This guide is meant purely as a demonstration of server-side routing. Do not assume that copying these examples will produce a fully functional setup in a production. It does not cover all capabilities of Xray-core, nor is it guaranteed to work perfectly in every environment.

For full details on configuration syntax and behavior, refer to the official Xray documentation. :::

Some users may have noticed that Remnawave automatically clears the clients array from the Node's configuration. This is not a bug, but rather an intended behavior. Remnawave clears it to ensure that only necessary configurations are retained for certain features to function properly.


In this guide, we'll walk through setting up server-side traffic routing using two Nodes: RU and DE. Users will connect to the RU Node. From there, the traffic will be routed as follows:

  • Traffic to .ru websites will be proxied directly through the RU Node.
  • All other traffic will be routed through the DE Node.

To achieve this, we'll create a service user that will facilitate the traffic routing.

As a result, the traffic "flow" could look something like this:

  1. The user opens google.com.
  2. The request first reaches the RU Node.
  3. The RU Node applies the routing rules. Since google.com is not a .ru domain, the traffic is routed to the DE Node.
  4. The DE Node completes the connection to google.com.

This type of server-side routing setup is often referred to as a "bridge".

Create a Config Profile for DE

Navigate to the Config Profiles section and create a new Config Profile, e.g. DE Bridge Profile.

{
    "log": {
        "loglevel": "warning"
    },
    "dns": {},
    "inbounds": [
        {
            "tag": "BRIDGE_DE_IN",
            "port": 9999,
            "listen": "0.0.0.0",
            "protocol": "shadowsocks",
            "settings": {
                "clients": [],
                "network": "tcp,udp"
            },
            "sniffing": {
                "enabled": true,
                "destOverride": ["http", "tls", "quic"]
            }
        }
    ],
    "outbounds": [
        {
            "tag": "DIRECT",
            "protocol": "freedom"
        },
        {
            "tag": "BLOCK",
            "protocol": "blackhole"
        }
    ],
    "routing": {
        "rules": []
    }
}

:::warning Don't Forget to Assign the Profile and Create an Internal Squad Assign the newly created Config Profile to the DE Node. Select the BRIDGE_DE_IN Inbound.


Create a new Internal Squad, let's call it Bridge Squad. Enable the Inbound we created earlier — BRIDGE_DE_IN. :::

Create a Service User

For our bridge to work, we will need to create a user. Let's call it bridge_user.

:::warning Don't Forget to Remove Traffic Limits and Extend the Expiry Date Since this is a service user, we obviously don't want its subscription to expire or be limited by traffic usage. Set the Data Limit to 0 and Expiry Date to year 2099. :::

Next, assign the appropriate Squad to this user. In our case, that's Bridge Squad.

After the service user is created, we will need to get its password. Open the user card, click on the More Actions button, then Detailed Info. There you should see Connection Information section.

Depending on the Inbound's protocol (Shadowsocks in our case), copy the appropriate value:

Protocol Connection Info Value
Trojan Trojan Password
VLESS VLESS UUID
Shadowsocks SS Password

Configure a Public Profile

Most likely, you already have a Config Profile that your users connect to. If not, refer to this guide.

For this step, we're not interested in the "inbounds" — instead, we'll be modifying the "outbounds", "routing", and "rules" arrays of that Profile.

{
    "log": {
        "loglevel": "none"
    },
    "inbounds": [
        {
            "tag": "PUBLIC_RU_INBOUND",
            "port": 443,
            "listen": "0.0.0.0",
            "protocol": "vless",
            "settings": {
                "clients": [],
                "decryption": "none"
            },
            "sniffing": {
                "enabled": true,
                "destOverride": ["http", "tls", "quic"]
            },
            "streamSettings": {
                "network": "raw",
                "security": "reality",
                "realitySettings": {
                    "target": "USE YOUR OWN VALUE!",
                    "show": false,
                    "xver": 0,
                    "shortIds": [""],
                    "privateKey": "USE YOUR OWN KEY!",
                    "serverNames": ["USE YOUR OWN VALUES!"]
                }
            }
        }
    ],
    "outbounds": [
        {
            "protocol": "freedom",
            "tag": "DIRECT"
        },
        {
            "protocol": "blackhole",
            "tag": "BLOCK"
        }
    ],
    "routing": {
        "rules": [
            {
                "ip": ["geoip:private"],
                "outboundTag": "BLOCK"
            },
            {
                "domain": ["geosite:private"],
                "outboundTag": "BLOCK"
            },
            {
                "protocol": ["bittorrent"],
                "outboundTag": "BLOCK"
            }
        ]
    }
}

Outbounds

First we need to modify the outbounds array.

{
    "tag": "SS_OUTBOUND_TO_DE",
    "protocol": "shadowsocks",
    "settings": {
        "servers": [
            {
                "address": "DE NODE ADDRESS",
                "password": "PASSWORD FROM PREVIOUS STEP",
                "port": 9999,
                "level": 0,
                "method": "chacha20-ietf-poly1305"
            }
        ]
    }
}
Field Value
address The IP address or domain name of your DE Node server.
port The Inbound port, in our case the port of BRIDGE_DE_IN.
password The password for the service user. Depends on the protocol you are using; see the previous step.
method Encryption method. For Shadowsocks in Remnawave, always use chacha20-ietf-poly1305.

Routing and Rules

Next, modify the routing rules according to your needs.

Below is the example of a rule that makes the RU Node an exit node for Russian IPs and websites, while the rest of the traffic gets sent to DE Node.

{
    "ip": ["geoip:ru"],
    "outboundTag": "DIRECT"
},
{
    "domain": ["geosite:category-ru"],
    "outboundTag": "DIRECT"
}

Below is the example of a rule that proxies all traffic to SS_OUTBOUND_TO_DE Outbound.

{
    "inboundTag": ["PUBLIC_RU_INBOUND"],
    "outboundTag": "SS_OUTBOUND_TO_DE"
}

The complete public Config Profile could look like this:

{
    "log": {
        "loglevel": "none"
    },
    "inbounds": [
        {
            "tag": "PUBLIC_RU_INBOUND",
            "port": 443,
            "listen": "0.0.0.0",
            "protocol": "vless",
            "settings": {
                "clients": [],
                "decryption": "none"
            },
            "sniffing": {
                "enabled": true,
                "destOverride": ["http", "tls", "quic"]
            },
            "streamSettings": {
                "network": "raw",
                "security": "reality",
                "realitySettings": {
                    "target": "USE YOUR OWN VALUE!",
                    "show": false,
                    "xver": 0,
                    "shortIds": [""],
                    "privateKey": "USE YOUR OWN KEY!",
                    "serverNames": ["USE YOUR OWN VALUES!"]
                }
            }
        }
    ],
    "outbounds": [
        {
            "protocol": "freedom",
            "tag": "DIRECT"
        },
        {
            "protocol": "blackhole",
            "tag": "BLOCK"
        },
        {
            "tag": "SS_OUTBOUND_TO_DE",
            "protocol": "shadowsocks",
            "settings": {
                "servers": [
                    {
                        "address": "DE NODE ADDRESS",
                        "password": "PASSWORD FROM PREVIOUS STEP",
                        "port": 9999,
                        "level": 0,
                        "method": "chacha20-ietf-poly1305"
                    }
                ]
            }
        }
    ],
    "routing": {
        "rules": [
            {
                "ip": ["geoip:private"],
                "outboundTag": "BLOCK"
            },
            {
                "domain": ["geosite:private"],
                "outboundTag": "BLOCK"
            },
            {
                "protocol": ["bittorrent"],
                "outboundTag": "BLOCK"
            },
            {
                "ip": ["geoip:ru"],
                "outboundTag": "DIRECT"
            },
            {
                "domain": ["geosite:category-ru"],
                "outboundTag": "DIRECT"
            },
            {
                "inboundTag": ["PUBLIC_RU_INBOUND"],
                "outboundTag": "SS_OUTBOUND_TO_DE"
            }
        ]
    }
}

:::tip Xray routing rules are processed in order, from top to bottom.
Here's how the rules in our example work:

  1. Private IPs (geoip:private) → BLOCK
    Traffic matching this rule is sent to the BLOCK Outbound, which is a blackhole protocol (completely blocked).

  2. Private domains (geosite:private) → BLOCK
    Same as above, but for domain names.

  3. Bittorrent traffic (bittorrent) → BLOCK
    All BitTorrent traffic is blocked.

  4. Russian IPs (geoip:ru) → DIRECT
    Traffic matching this rule is sent to the DIRECT Outbound, which is a freedom protocol (RU Node acts as an exit node).

  5. Russian domains (geosite:category-ru) → DIRECT
    Traffic to Russian domains also goes directly out from the RU Node.

  6. No rule match (the rest of the traffic) → SS_OUTBOUND_TO_DE
    Any remaining traffic arriving at the PUBLIC_RU_INBOUND is routed to the DE Node via the Shadowsocks Outbound we created earlier.


You don't have to use Shadowsocks as the transit protocol — you can use VLESS instead. Just make sure to update your configuration accordingly. :::