Operating system
Windows
System version
1.13.0
Installation type
Original sing-box Command Line
If you are using a graphical client, please provide the version of the client.
https://github.com/Leadaxe/singbox-launcher/releases/tag/v0.8.2
Version
Description
About
I'm the author of singbox-launcher —
a cross-platform GUI launcher for sing-box written in Go (300+ ★).
I'm working on adding WireGuard endpoint support to the GUI,
and ran into this issue while testing WG configs via CLI before implementing the feature.
Environment
- sing-box version: latest stable
- OS: Windows 11
- Mode: TUN (auto_route + strict_route) + WireGuard endpoint with
system: true
- Tool: singbox-launcher — GUI launcher for sing-box (Go, Fyne)
Config
config.json (keys redacted)
{
"log": { "level": "info", "timestamp": true },
"dns": {
"servers": [
{ "type": "udp", "tag": "direct_dns_resolver", "server": "1.1.1.1", "server_port": 53 }
],
"rules": [{ "clash_mode": "direct", "server": "direct_dns_resolver" }],
"final": "direct_dns_resolver",
"independent_cache": true
},
"inbounds": [
{
"type": "tun", "tag": "tun-in",
"address": ["172.19.0.1/30"],
"auto_route": true, "strict_route": true, "stack": "mixed",
"route_exclude_address": ["212.x.x.x/32"]
}
],
"endpoints": [
{
"type": "wireguard", "tag": "wg-ep", "name": "singbox-wg0",
"system": true, "mtu": 1420,
"address": ["10.10.10.2/32"],
"private_key": "...",
"listen_port": 10000,
"peers": [
{
"address": "212.x.x.x", "port": 51820,
"public_key": "...",
"allowed_ips": ["0.0.0.0/0", "::/0"],
"persistent_keepalive_interval": 25
}
]
}
],
"outbounds": [
{ "type": "direct", "tag": "wg-out", "bind_interface": "singbox-wg0" },
{ "type": "direct", "tag": "direct-out" }
],
"route": {
"rules": [
{ "action": "sniff" },
{ "protocol": "dns", "action": "hijack-dns" }
],
"auto_detect_interface": true,
"final": "wg-out"
}
}
Expected behavior
All traffic routes through WireGuard tunnel (singbox-wg0). ping 1.1.1.1 works.
Actual behavior
ping 212.x.x.x (WG server) — works (4ms, physical route)
ping 1.1.1.1 — hangs, no response
ping 192.168.100.1 (WG peer network) — timeout
WG handshake appears to complete (logs show endpoint started, keepalive active), but return traffic never arrives.
Root cause (investigated)
The tun interface installs a default route 0.0.0.0/0 → 172.19.0.2 with metric 0. This captures all traffic including WireGuard handshake UDP packets destined for the WG server, creating a routing loop:
WG handshake → tun → sing-box → wg-out → singbox-wg0 → [needs to reach 212.x.x.x] → tun → loop
What doesn't work
route_exclude_address: ["212.x.x.x/32"] — does NOT work as expected.
Instead of adding a single /32 exclusion, sing-box tun creates many aggregate routes covering the surrounding address space, but not the exact address itself. The WG server IP still gets routed through tun.
Example of what actually appears in the routing table after adding route_exclude_address:
route print shows ~20 aggregate routes around 212.x.x.x, but 212.x.x.x/32 itself is absent
Workaround
Manual OS-level route after each sing-box start:
route add 212.x.x.x mask 255.255.255.255 192.168.1.1 metric 5
This is fragile — requires running after every restart and is not automatable within sing-box config.
Expected fix / question
Is there a way to properly exclude a single IP from tun routing so that WireGuard handshake traffic bypasses the tun interface entirely?
route_exclude_address behavior with exact /32 addresses seems like a bug — it should add a host route for that exact IP, not aggregate routes.
Reproduction
Reproduction steps
Prerequisites
- Windows 10/11
- sing-box latest version (run as Administrator)
- Any working WireGuard server (use your own keys/endpoint)
Steps
- Create
config.json, substituting your own WireGuard credentials:
{
"log": { "level": "info", "timestamp": true },
"inbounds": [
{
"type": "tun",
"tag": "tun-in",
"address": ["172.19.0.1/30"],
"auto_route": true,
"strict_route": true,
"stack": "mixed",
"route_exclude_address": ["<YOUR_WG_SERVER_IP>/32"]
}
],
"endpoints": [
{
"type": "wireguard",
"tag": "wg-ep",
"name": "singbox-wg0",
"system": true,
"mtu": 1420,
"address": ["<YOUR_WG_CLIENT_IP>/32"],
"private_key": "<YOUR_PRIVATE_KEY>",
"listen_port": 10000,
"peers": [
{
"address": "<YOUR_WG_SERVER_IP>",
"port": <YOUR_WG_PORT>,
"public_key": "<YOUR_SERVER_PUBLIC_KEY>",
"allowed_ips": ["0.0.0.0/0", "::/0"],
"persistent_keepalive_interval": 25
}
]
}
],
"outbounds": [
{ "type": "direct", "tag": "wg-out", "bind_interface": "singbox-wg0" },
{ "type": "direct", "tag": "direct-out" }
],
"route": {
"rules": [
{ "action": "sniff" },
{ "protocol": "dns", "action": "hijack-dns" }
],
"auto_detect_interface": true,
"final": "wg-out"
}
}
- Run as Administrator:
sing-box run -c config.json
- In a separate cmd window, check routing table:
- Try to ping any public IP:
Expected result
route print shows exact host route for <YOUR_WG_SERVER_IP>/32 bypassing tun
ping 1.1.1.1 replies through WireGuard tunnel
Observed result
route print shows aggregate routes around the server IP, but no exact /32 entry for it
ping 1.1.1.1 hangs — packets are sent but no replies received
ping <YOUR_WG_SERVER_IP> works (physical route exists)
Workaround (manual, required after every start)
route add <YOUR_WG_SERVER_IP> mask 255.255.255.255 <YOUR_GATEWAY_IP> metric 5
After adding this route manually, tunnel works correctly.
Logs
Supporter
Integrity requirements
Operating system
Windows
System version
1.13.0
Installation type
Original sing-box Command Line
If you are using a graphical client, please provide the version of the client.
https://github.com/Leadaxe/singbox-launcher/releases/tag/v0.8.2
Version
Description
About
I'm the author of singbox-launcher —
a cross-platform GUI launcher for sing-box written in Go (300+ ★).
I'm working on adding WireGuard endpoint support to the GUI,
and ran into this issue while testing WG configs via CLI before implementing the feature.
Environment
system: trueConfig
config.json (keys redacted)
{ "log": { "level": "info", "timestamp": true }, "dns": { "servers": [ { "type": "udp", "tag": "direct_dns_resolver", "server": "1.1.1.1", "server_port": 53 } ], "rules": [{ "clash_mode": "direct", "server": "direct_dns_resolver" }], "final": "direct_dns_resolver", "independent_cache": true }, "inbounds": [ { "type": "tun", "tag": "tun-in", "address": ["172.19.0.1/30"], "auto_route": true, "strict_route": true, "stack": "mixed", "route_exclude_address": ["212.x.x.x/32"] } ], "endpoints": [ { "type": "wireguard", "tag": "wg-ep", "name": "singbox-wg0", "system": true, "mtu": 1420, "address": ["10.10.10.2/32"], "private_key": "...", "listen_port": 10000, "peers": [ { "address": "212.x.x.x", "port": 51820, "public_key": "...", "allowed_ips": ["0.0.0.0/0", "::/0"], "persistent_keepalive_interval": 25 } ] } ], "outbounds": [ { "type": "direct", "tag": "wg-out", "bind_interface": "singbox-wg0" }, { "type": "direct", "tag": "direct-out" } ], "route": { "rules": [ { "action": "sniff" }, { "protocol": "dns", "action": "hijack-dns" } ], "auto_detect_interface": true, "final": "wg-out" } }Expected behavior
All traffic routes through WireGuard tunnel (
singbox-wg0).ping 1.1.1.1works.Actual behavior
ping 212.x.x.x(WG server) — works (4ms, physical route)ping 1.1.1.1— hangs, no responseping 192.168.100.1(WG peer network) — timeoutWG handshake appears to complete (logs show endpoint started, keepalive active), but return traffic never arrives.
Root cause (investigated)
The tun interface installs a default route
0.0.0.0/0 → 172.19.0.2with metric 0. This captures all traffic including WireGuard handshake UDP packets destined for the WG server, creating a routing loop:WG handshake → tun → sing-box → wg-out → singbox-wg0 → [needs to reach 212.x.x.x] → tun → loop
What doesn't work
route_exclude_address: ["212.x.x.x/32"]— does NOT work as expected.Instead of adding a single
/32exclusion, sing-box tun creates many aggregate routes covering the surrounding address space, but not the exact address itself. The WG server IP still gets routed through tun.Example of what actually appears in the routing table after adding
route_exclude_address:route print shows ~20 aggregate routes around 212.x.x.x, but 212.x.x.x/32 itself is absent
Workaround
Manual OS-level route after each sing-box start:
This is fragile — requires running after every restart and is not automatable within sing-box config.
Expected fix / question
Is there a way to properly exclude a single IP from tun routing so that WireGuard handshake traffic bypasses the tun interface entirely?
route_exclude_addressbehavior with exact/32addresses seems like a bug — it should add a host route for that exact IP, not aggregate routes.Reproduction
Reproduction steps
Prerequisites
Steps
config.json, substituting your own WireGuard credentials:{ "log": { "level": "info", "timestamp": true }, "inbounds": [ { "type": "tun", "tag": "tun-in", "address": ["172.19.0.1/30"], "auto_route": true, "strict_route": true, "stack": "mixed", "route_exclude_address": ["<YOUR_WG_SERVER_IP>/32"] } ], "endpoints": [ { "type": "wireguard", "tag": "wg-ep", "name": "singbox-wg0", "system": true, "mtu": 1420, "address": ["<YOUR_WG_CLIENT_IP>/32"], "private_key": "<YOUR_PRIVATE_KEY>", "listen_port": 10000, "peers": [ { "address": "<YOUR_WG_SERVER_IP>", "port": <YOUR_WG_PORT>, "public_key": "<YOUR_SERVER_PUBLIC_KEY>", "allowed_ips": ["0.0.0.0/0", "::/0"], "persistent_keepalive_interval": 25 } ] } ], "outbounds": [ { "type": "direct", "tag": "wg-out", "bind_interface": "singbox-wg0" }, { "type": "direct", "tag": "direct-out" } ], "route": { "rules": [ { "action": "sniff" }, { "protocol": "dns", "action": "hijack-dns" } ], "auto_detect_interface": true, "final": "wg-out" } }ping 1.1.1.1Expected result
route printshows exact host route for<YOUR_WG_SERVER_IP>/32bypassing tunping 1.1.1.1replies through WireGuard tunnelObserved result
route printshows aggregate routes around the server IP, but no exact/32entry for itping 1.1.1.1hangs — packets are sent but no replies receivedping <YOUR_WG_SERVER_IP>works (physical route exists)Workaround (manual, required after every start)
After adding this route manually, tunnel works correctly.
Logs
Supporter
Integrity requirements