---
title: Outbounds & Routing
description: Shape egress in 3x-ui — WARP, NordVPN, PIA WireGuard, outbound subscriptions, routing rules, and load balancers.
icon: Route
---

Inbounds accept clients; **outbounds** decide where their traffic goes next.
3x-ui can route traffic through Cloudflare WARP, NordVPN, Private Internet Access
(WireGuard), or arbitrary outbound pools imported from a subscription,
and select between them with routing rules and balancers.

## Editing outbounds & routing

Outbounds, routing rules, balancers, DNS, and logging all live in the **Xray
configuration** (the config template you edit under Xray Settings). There's no
separate per-rule UI — you edit the JSON, and the panel reloads Xray. The panel
also offers an **outbound connectivity test** and a **route test** (ask the
running core which outbound a given destination would use).

## Build an outbound

Every outbound is a JSON object with up to four parts: a **`tag`** (referenced by
routing rules and balancers), a **`protocol`**, protocol-specific **`settings`**,
and — for proxy protocols — **`streamSettings`** that must match the remote
inbound's transport and security. Two outbounds are almost always present:

- **`freedom`** sends traffic straight to its destination — the default egress.
  Optionally set a `domainStrategy` (e.g. `UseIP`) to control how hostnames resolve.
- **`blackhole`** drops traffic. Route unwanted destinations (ads, torrents) here.

```json title="freedom + blackhole"
{
  "outbounds": [
    { "tag": "direct", "protocol": "freedom", "settings": {} },
    { "tag": "block", "protocol": "blackhole", "settings": {} }
  ]
}
```

A **proxy** outbound (VLESS, VMess, Trojan, Shadowsocks) forwards to another
server — handy for chaining or sending select traffic abroad. Mind the wire
shapes 3x-ui uses: **VLESS is the flat form** (`address`/`port`/`id`/`flow`/
`encryption`), **VMess uses `settings.vnext[]`**, and **Trojan/Shadowsocks use
`settings.servers[]`**. The `streamSettings` must mirror the destination's
[transport and security](/docs/config/transports).

Assemble any outbound below and paste the JSON into **Xray Settings → Outbounds**:

<OutboundGenerator />

## Cloudflare WARP

WARP lets your server egress through Cloudflare's network. 3x-ui can register a
WARP account for you and wire it into a WireGuard outbound tagged **`warp`**:

<Steps>

<Step>
### Add a `warp`-tagged outbound

Create a WireGuard outbound with the tag `warp` in your Xray config.
</Step>

<Step>
### Register WARP

From the panel's WARP controls, register an account. 3x-ui fills the outbound's
keys, addresses, reserved bytes, and peer endpoint automatically.
</Step>

<Step>
### (Optional) auto-rotate the IP

Set a WARP update interval (in **days**) to periodically rotate the WARP IP. A
free license can also be applied.
</Step>

</Steps>

Route the traffic you want (for example specific domains) to the `warp` outbound
with a routing rule.

## NordVPN

3x-ui can fetch NordVPN (NordLynx/WireGuard) credentials from an access token (or
accept a private key directly) and list countries/servers, so you can build a
NordVPN outbound. Open **Xray → Outbounds → More → NordVPN**, sign in or save a
private key, select a server, and add the outbound. You can add several servers;
each hostname has a unique `nord-<hostname>` tag and cannot be added twice.

**Reset** on an added row keeps its server, tag, peer, and routing references but
refreshes its embedded private key from the currently stored NordVPN credentials.
Logout clears only those stored credentials. Existing outbounds continue to use
their embedded keys; remove unused NordVPN outbounds from the Outbounds list.

## PIA WireGuard

3x-ui can sign in with a PIA username and password, list countries/regions/servers
from the signed PIA server list, and build a WireGuard outbound. Open
**Xray → Outbounds → More → PIA**, sign in, pick a server, and add the outbound.
You can add several servers (one outbound per hostname). The tag is
`pia-<region>-<server>` (for example `pia-us-east-useast1`). Adding or using
**Reset** on a row registers a WireGuard key with PIA `/addKey` for that server.
The same hostname cannot be added twice. Logout clears the stored token only;
delete unused PIA outbounds from the Outbounds list. Reset and delete do not
revoke the WireGuard peer on the PIA account.

The password is not stored. The PIA API token is stored with the same
`NODE_TOKEN_ENCRYPTION` setting as node API tokens. If you retire an old
`XUI_NODE_TOKEN_KEY` without signing into PIA again, Add/Reset fail until you
re-login. Peer `allowedIPs` is IPv4-only (`0.0.0.0/0`).

## Outbound subscriptions (server pools)

An **outbound subscription** imports a remote share-link subscription and injects
its servers as **outbounds** into the running Xray config — without touching your
saved template. This is the recommended way to subscribe to a *pool* of servers.

| Field            | Default | Meaning                                                        |
| ---------------- | ------- | -------------------------------------------------------------- |
| `url`            | —       | The remote subscription URL (SSRF-guarded).                    |
| `tagPrefix`      | auto    | Prefix for generated outbound tags (e.g. `hk-`); blank = `subN-`. |
| `updateInterval` | `600`   | Refresh interval in **seconds**.                               |
| `prepend`        | `false` | Place these outbounds before your manual ones.                 |
| `priority`       | `0`     | Merge order (lower first).                                     |

Imported outbounds get **stable tags**: the same server keeps the same tag across
refreshes, so exact-tag routing/balancer selectors stay pinned — while
prefix/wildcard selectors (e.g. `hk-*`) automatically pick up new servers as the
pool changes. Supported link schemes: `vmess`, `vless`, `trojan`, `ss`,
`hysteria2` (`hy2`), and `wireguard` (`wg`). The panel refreshes enabled
subscriptions on a timer and reloads Xray when something changes.

## Routing rules

**Routing rules** decide which outbound (or balancer) each connection uses. Each
rule is a `field`-type matcher: set any of `domain`, `ip`, `port`, `network`,
`protocol`, `inboundTag`, `sourceIP`, … and point it at an **`outboundTag`** or a
**`balancerTag`**. Rules are evaluated **top-to-bottom — the first match wins**, so
put specific rules above general ones.

```json title="route ads to blackhole, private IPs direct"
{
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      { "type": "field", "domain": ["geosite:category-ads-all"], "outboundTag": "block" },
      { "type": "field", "ip": ["geoip:private"], "outboundTag": "direct" }
    ]
  }
}
```

## Balancers

A **balancer** groups outbounds by a **selector** (tag prefixes, including the
wildcard pools from outbound subscriptions) and spreads or fails traffic over them
with a **strategy**:

| Strategy      | Picks…                                          | Needs a monitor          |
| ------------- | ----------------------------------------------- | ------------------------ |
| `random`      | a random member per connection                  | no                       |
| `roundRobin`  | members in rotation                             | no                       |
| `leastPing`   | the lowest-latency member                       | **`observatory`**        |
| `leastLoad`   | the most stable member by sampled load          | **`burstObservatory`**   |

Reference a balancer from a rule via `balancerTag`. `leastPing` and `leastLoad`
need a health monitor, which Xray places at the **top level** of the config
(`observatory` / `burstObservatory`, **not** inside `routing`). The panel can
report balancer status and **override** a balancer to a specific outbound for
testing.

Build the routing block — rules, balancers, and the matching observatory — here:

<RoutingBuilder />

<Callout type="warn">
  Outbounds that reach external services are fetched with SSRF protection — by
  default private/internal addresses are blocked unless you explicitly allow them
  per source.
</Callout>
