Routing — sing-box
sing-box's route block holds rules, rule-sets, and a handful of default-interface / process-lookup toggles. Rules are polymorphic (default + logical) and carry an explicit action selecting one of eight behaviors.
Top-level options
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
geoip | *GeoIPOptions | (ignored) | GeoIPOptions | GeoIP database is not supported: the block is parsed but ignored; use rule-sets. |
geosite | *GeositeOptions | (ignored) | GeositeOptions | GeoSite database is not supported: the block is parsed but ignored; use rule-sets. |
rules | []Rule | [] | [Rule] | Routing rules, evaluated in order. |
rule_set | []RuleSet | [] | [RuleSet] | Named rule-sets — referenced from rules via rule_set match keys. Three types: inline, local, remote. |
final | string | (unset) | <outbound tag> | Default outbound when no rule matches. When unset, the first outbound in outbounds is used. |
find_process | bool | false | true | false | Look up the originating process for every connection. Required by process_name / process_path rules. |
find_neighbor | bool | false | true | false | Linux / macOS. Force neighbor resolution (MAC address / hostname of LAN clients) for logging even when no source_mac_address / source_hostname rule exists. |
dhcp_lease_files | badoption.Listable[string] | (auto-detected) | [<file path>] | Linux / macOS. DHCP lease files used to map LAN clients to hostnames and MAC addresses. Auto-detected for dnsmasq, odhcpd, ISC dhcpd and Kea when empty. |
auto_detect_interface | bool | false | true | false | Auto-discover the system's default outbound interface — used by Direct outbounds without bind_interface. |
override_android_vpn | bool | false | true | false | Android only — bypass the system VPN service for outbound dials. |
default_interface | string | (auto) | <interface> | Override the default outbound interface. Overrides auto_detect_interface. |
default_mark | FwMark | 0 | <uint32> | Linux SO_MARK applied to outbound sockets. |
default_domain_resolver | *DomainResolveOptions | (none) | DomainResolveOptions | Default resolver for destination domains when no rule specifies one. |
default_network_strategy | *NetworkStrategy | (unset) | NetworkStrategy | Default network-strategy used by Happy-Eyeballs / cellular-vs-wifi dial races. |
default_network_type | badoption.Listable[InterfaceType] | [] | <InterfaceType> | Preferred network types for the default outbound. |
default_fallback_network_type | badoption.Listable[InterfaceType] | [] | <InterfaceType> | Fallback network types when the preferred one fails. |
default_fallback_delay | badoption.Duration | 0 | <duration> | Delay before switching to a fallback network. |
default_http_client | string | (first http_clients entry) | <http client tag> | Tag of the top-level http_clients entry used by remote rule-sets that set no http_client. When no http_clients exist at all, a deprecated implicit client dialing through the default outbound is used. |
Source: option/route.go:5-24 · pinned at v1.14.2 (af6e64c)
Rules
Each entry in rules[] is a polymorphic Rule object. The type field decides the shape:
type: "default"(or omitted) — flatRawDefaultRulewith match fields + aRuleAction.type: "logical"— boolean combinator over nested rules.
Default rule — match fields
RawDefaultRule has 44 fields, all optional and AND-combined when multiple are set. The most-used:
| Field | Type | Description |
|---|---|---|
inbound | []string | Match by inbound tag. |
network | []string | tcp, udp, or tcp,udp. |
protocol | []string | Sniffed application protocol. |
domain / domain_suffix / domain_keyword / domain_regex | []string | Domain matchers. |
geosite / geoip / source_geoip | []string | Not supported — a rule using them fails at startup. Use rule_set instead. |
ip_cidr / source_ip_cidr | []string | CIDR match. |
ip_is_private / source_ip_is_private | bool | Match RFC1918 / link-local / loopback. |
port / source_port | []uint16 | Discrete-port match. |
port_range / source_port_range | []string | Port-range match (80:90, 1024:, …). |
process_name / process_path / process_path_regex | []string | Process match (needs find_process: true). |
package_name | []string | Android package name (UID resolved via the system API). package_name_regex matches by regular expression. |
source_mac_address / source_hostname | []string | Source device MAC address / DHCP hostname, via neighbor resolution (Linux, macOS, or the Android / macOS graphical clients). |
user / user_id | []string / []int32 | Local user / UID. |
clash_mode | string | Match only when the runtime mode (controlled via the Clash API) matches this string. |
wifi_ssid / wifi_bssid | []string | Wifi-aware routing (mobile-only). |
network_is_expensive / network_is_constrained | bool | iOS/macOS network-type flags. |
rule_set | []string | Match if any of the named rule-sets matches. |
invert | bool | Invert the entire rule's match result. |
Logical rule
{
"type": "logical",
"mode": "and",
"rules": [ <Rule>, <Rule>, … ],
"invert": false,
"action": "..."
}mode is and (default) or or. Nested rules can themselves be logical.
Rule action
Every rule carries an action that decides what happens on match. Eight action values:
| Action | Meaning |
|---|---|
route (default) | Send to outbound. Extra fields: override_address, override_port, network_strategy, udp_*, tls_fragment*, tls_spoof / tls_spoof_method. |
route-options | Apply route options to subsequent matching without leaving the rule chain. |
direct | Direct dial — bypass outbounds entirely (uses default_interface etc.). |
bypass | Same shape as route, deliberately distinct for logging. |
reject | Drop the connection. With method: "drop" or method: "default". |
hijack-dns | Hijack the connection as if it were DNS, routing to the DNS engine. |
sniff | Run protocol sniffing on the connection. It also runs in pre-match for UDP connections from L3 inbounds (TUN, WireGuard, Tailscale), on the first packet. |
resolve | Resolve the destination domain via the named DNS server before further rules run. Optional timeout and disable_optimistic_cache. |
Rule-sets
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
type | string | inline | inline | local | remote | Where the rule set lives. inline holds the rules right in this config; local reads from a file path; remote downloads from a URL. |
tag | badoption.Listable[string] | (required) | <string> | [<string>] | Reference name used by rules' rule_set match key. A list defines several rule-sets sharing the other options; {tag} in path, url or initial_path is replaced by each tag (required with multiple tags; not allowed with type: inline). |
format | string | (inferred) | source | binary | File format. source is JSON; binary is the compiled .srs format. Inferred from the file extension when unset. |
Source: option/rule_set.go:22-29 · pinned at v1.14.2 (af6e64c)
Local rule-set
{ "type": "local", "tag": "cn", "format": "binary", "path": "geosite-cn.srs" }Remote rule-set
{
"type": "remote",
"tag": "cn",
"format": "binary",
"url": "https://example.com/geosite-cn.srs",
"http_client": { "detour": "direct" },
"update_interval": "168h"
}Inline rule-set
{
"type": "inline",
"tag": "block",
"rules": [
{ "domain_keyword": ["ads", "tracker"] }
]
}The inline form's rules are HeadlessRule — structurally identical to routing rules but without the action field (the action is whatever the referencing rule does).
Examples
CN-direct + everything else through proxy:
{
"route": {
"rule_set": [
{ "type": "remote", "tag": "geoip-cn", "format": "binary",
"url": "https://github.com/SagerNet/sing-geoip/raw/rule-set/geoip-cn.srs" },
{ "type": "remote", "tag": "geosite-cn", "format": "binary",
"url": "https://github.com/SagerNet/sing-geosite/raw/rule-set/geosite-cn.srs" }
],
"rules": [
{ "ip_is_private": true, "outbound": "direct" },
{ "rule_set": ["geoip-cn", "geosite-cn"], "outbound": "direct" },
{ "action": "sniff" },
{ "protocol": "dns", "action": "hijack-dns" }
],
"final": "proxy",
"find_process": false,
"auto_detect_interface": true
}
}Reject ads with an inline rule-set:
{
"route": {
"rule_set": [
{
"type": "inline",
"tag": "ads",
"rules": [
{ "domain_keyword": ["doubleclick", "googlesyndication"] }
]
}
],
"rules": [
{ "rule_set": "ads", "action": "reject" }
]
}
}Logical OR rule:
{
"type": "logical",
"mode": "or",
"rules": [
{ "domain_suffix": [".onion"] },
{ "rule_set": ["tor-exit"] }
],
"outbound": "tor"
}Notes
geoip/geositeare not supported: the top-level blocks are parsed but ignored, andgeoip/geosite/source_geoiprule items make the rule fail at startup. Load equivalent data viarule_set(remote.srsfiles) and reference it through the rule'srule_setmatch field.- Remote rule-sets download through an HTTP client:
http_clienttakes an inline object (engine, TLS and dial fields such asdetour) or the tag of a top-levelhttp_clientsentry; without it,route.default_http_clientor the firsthttp_clientsentry is used.download_detouris deprecated in favour ofhttp_client.initial_pathseeds the rule-set from a local file so startup isn't blocked by the first download. - Rule-set matching: a referenced rule-set's fields are merged into the referencing rule only when it holds exactly one
defaultrule withoutinvert; any other rule-set is evaluated as a standalone condition that matches when any one of its rules matches. source_mac_address/source_hostnamerely on neighbor resolution, which turns on automatically when such rules (or a local DNS server'sneighbor_domain) exist;find_neighborforces it for logging, anddhcp_lease_filessupplies hostnames on Linux / macOS.rule_set_ip_cidr_match_source(snake_case in the struct) controls whether a rule-set's IP-CIDR rules match source or destination. The aliasrule_set_ipcidr_match_sourceis deprecated.- The
sniffandresolverule actions are typically used at the head of the rule list to ensure later rules see useful metadata. The DNS engine relies onhijack-dnsto capture DNS queries the routing engine wants to handle. clash_modeonly takes effect if the Clash API is enabled — that's where the mode comes from.
Cross-core notes
- Xray-core uses a single polymorphic rule shape with camelCase field names and a much smaller match-key set. There is no
actionenum — every rule routes to anoutboundTagorbalancerTag. See Routing — Xray-core. - mihomo uses compact one-line string rules (
DOMAIN-SUFFIX,example.com,proxy) and a separaterule-providers:mechanism for remote rule lists. See Routing — mihomo.
Source: option/route.go:5-24 · v1.14.2 (af6e64c)
