[GH-ISSUE #4950] Windows subnet route does not bypass local LAN traffic (unlike Tailscale) #10419

Open
opened 2026-08-05 01:25:51 -04:00 by saavagebueno · 8 comments
Owner

Originally created by @cheetah747 on GitHub (Dec 15, 2025).
Original GitHub issue: https://github.com/netbirdio/netbird/issues/4950

Describe the problem

When using subnet routes on Windows, NetBird currently forwards traffic to the overlay interface even when both peers are on the same local LAN, causing traffic to go through WireGuard / Relay unnecessarily.

This differs significantly from the behavior of Tailscale, which correctly detects same-LAN scenarios and bypasses the overlay, even when a subnet route with a lower metric exists.

To Reproduce

Steps to reproduce the behavior:

  1. setup my self-hosted netbird on vps
  2. install netbird client on windows PC and NAS that they are in the same local LAN
  3. setup the network routes by netbird dashboard to advertise the LAN local IP network(192.168.11.0/24),and set the NAS as the routing peer and the pc belongs the distribution groups.
  4. try to use the win PC to download the file from NAS,we can found that the traffic go through the relay server.
  5. turn off the "active" of the network routes, try downloading NAS file again,then it didn't go through the relay server
  6. run netbird status -d on the win PC and it shows NAS connection is relay ,even if the router on the LAN has opened the upnp . but when I turn off the network routes and it will be P2P successfully
  7. I have searched for similar issues on GitHub and somebody recommanded to set the firewall port, but even if I turned off the win PC's firewall it didn't works well.

Expected behavior

the traffic between two devices that on the same local LAN shouldn't go through the relay server(A -> LAN router -> relay server ->LAN router ->B), it should just go through the LAN router(A -> LAN router -> B),however the tailscale works well.

Are you using NetBird Cloud? No , I'm self-hosted

Please specify whether you use NetBird Cloud or self-host NetBird's control plane.

NetBird version

netbird version:client 0.60.4 / server management 0.60.7

Is any other VPN software installed?: Yes

If yes, which one? :tailscale (when running netbird,I have disabled tailscale)

Debug output

To help us resolve the problem, please attach the following anonymized status output

netbird status -dA
output:

Peers detail:
 vivo.netbird.selfhosted:
  NetBird IP: 100.100.0.34
  Public key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  Status: Connecting
  -- detail --
  Connection type: -
  ICE candidate (Local/Remote): -/-
  ICE candidate endpoints (Local/Remote): -/-
  Relay server address:
  Last connection update: 6 minutes, 36 seconds ago
  Last WireGuard handshake: -
  Transfer status (received/sent) 0 B/0 B
  Quantum resistance: false
  Networks: -
  Latency: 0s

 lenovo-notebook.netbird.selfhosted:
  NetBird IP: 100.100.0.60
  Public key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  Status: Connecting
  -- detail --
  Connection type: -
  ICE candidate (Local/Remote): -/-
  ICE candidate endpoints (Local/Remote): -/-
  Relay server address:
  Last connection update: 6 minutes, 36 seconds ago
  Last WireGuard handshake: -
  Transfer status (received/sent) 0 B/0 B
  Quantum resistance: false
  Networks: -
  Latency: 0s

 nas.netbird.selfhosted:
  NetBird IP: 100.100.0.177
  Public key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  Status: Connected
  -- detail --
  Connection type: Relayed
  ICE candidate (Local/Remote): -/-
  ICE candidate endpoints (Local/Remote): -/-
  Relay server address: rels://netbird.<mydomain>:61143/relay
  Last connection update: 6 minutes, 36 seconds ago
  Last WireGuard handshake: 29 seconds ago
  Transfer status (received/sent) 3.4 MiB/1.2 MiB
  Quantum resistance: false
  Networks: 192.168.11.0/24
  Latency: 0s

 office.netbird.selfhosted:
  NetBird IP: 100.100.0.187
  Public key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  Status: Connected
  -- detail --
  Connection type: Relayed
  ICE candidate (Local/Remote): -/-
  ICE candidate endpoints (Local/Remote): -/-
  Relay server address: rels://netbird.<mydomain>:61143/relay
  Last connection update: 6 minutes, 12 seconds ago
  Last WireGuard handshake: 1 minute, 58 seconds ago
  Transfer status (received/sent) 924 B/808 B
  Quantum resistance: false
  Networks: 172.16.0.0/16, 192.168.1.0/24
  Latency: 0s

Events:
  [INFO] SYSTEM (c7fd4fa1-a886-4f02-96b5-e547558d826a)
    Message: Network map updated
    Time: 2 hours, 48 minutes ago
  [INFO] SYSTEM (a2186d03-be31-44fb-8370-5062cf610c32)
    Message: Network map updated
    Time: 2 hours, 32 minutes ago
  [INFO] SYSTEM (323f0fd8-ddf8-4307-ab3f-25e9fc4f12b5)
    Message: Network map updated
    Time: 47 minutes, 57 seconds ago
  [INFO] SYSTEM (3c212068-2bd6-4793-81d8-e12c6f47ffb9)
    Message: Network map updated
    Time: 45 minutes, 38 seconds ago
  [INFO] SYSTEM (bce1262e-e9af-4e5d-ba13-b898e938d337)
    Message: Network map updated
    Time: 35 minutes, 27 seconds ago
  [INFO] SYSTEM (8b724771-b7c5-4edb-a858-8ae78645fc1d)
    Message: Network map updated
    Time: 20 minutes, 52 seconds ago
  [INFO] SYSTEM (10ae7f24-ba3a-4182-a6cb-b34d4b168dd2)
    Message: Network map updated
    Time: 18 minutes, 59 seconds ago
  [INFO] SYSTEM (04db39f6-9c78-4037-bddb-4267c3b5aa8c)
    Message: Network map updated
    Time: 18 minutes, 45 seconds ago
  [INFO] SYSTEM (8a120eb1-e42c-4eb0-afc3-80f62873a4dd)
    Message: Network map updated
    Time: 7 minutes, 1 seconds ago
  [INFO] SYSTEM (d4700ab2-fdb4-4483-b2f3-63e7d6c1f7b8)
    Message: Network map updated
    Time: 6 minutes, 36 seconds ago
OS: windows/amd64
Daemon version: 0.60.4
CLI version: 0.60.4
Profile: default
Management: Connected to https://netbird.<mydomain>:61143
Signal: Connected to https://netbird.<mydomain>:61143
Relays:
  [stun:netbird.<mydomain>:3478] is Available
  [turn:netbird.<mydomain>:3478?transport=udp] is Available
  [rels://netbird.<mydomain>:61143/relay] is Available
Nameservers:
FQDN: win-pc.netbird.selfhosted
NetBird IP: 100.100.0.155/24
Interface type: Userspace
Quantum resistance: false
Lazy connection: false
SSH Server: Disabled
Networks: -
Forwarding rules: 0
Peers count: 2/4 Connected

Create and upload a debug bundle, and share the returned file key:

netbird debug for 1m -AS -U

ac37d4641684b3657e68ca9c991147161e6873087035defacb573705b470b899/6a549cab-01c7-4400-aa8c-f67d70ac5e07

Uploaded files are automatically deleted after 30 days.

Alternatively, create the file only and attach it here manually:

netbird debug for 1m -AS

Screenshots

If applicable, add screenshots to help explain your problem.

Additional context

Add any other context about the problem here.

Have you tried these troubleshooting steps?

  • Reviewed client troubleshooting (if applicable)
  • Checked for newer NetBird versions
  • Searched for similar issues on GitHub (including closed ones)
  • Restarted the NetBird client
  • Disabled other VPN software
  • Checked firewall settings
Originally created by @cheetah747 on GitHub (Dec 15, 2025). Original GitHub issue: https://github.com/netbirdio/netbird/issues/4950 **Describe the problem** When using subnet routes on Windows, NetBird currently forwards traffic to the overlay interface even when both peers are on the same local LAN, causing traffic to go through WireGuard / Relay unnecessarily. This differs significantly from the behavior of Tailscale, which correctly detects same-LAN scenarios and bypasses the overlay, even when a subnet route with a lower metric exists. **To Reproduce** Steps to reproduce the behavior: 1. setup my self-hosted netbird on vps 2. install netbird client on windows PC and NAS that they are in the same local LAN 3. setup the `network routes` by netbird dashboard to advertise the LAN local IP network(192.168.11.0/24),and set the NAS as the routing peer and the pc belongs the distribution groups. 4. try to use the win PC to download the file from NAS,we can found that the traffic go through the relay server. 5. turn off the "active" of the `network routes`, try downloading NAS file again,then it didn't go through the relay server 6. run `netbird status -d` on the win PC and it shows NAS connection is relay ,even if the router on the LAN has opened the upnp . but when I turn off the `network routes` and it will be P2P successfully 7. I have searched for similar issues on GitHub and somebody recommanded to set the firewall port, but even if I turned off the win PC's firewall it didn't works well. **Expected behavior** the traffic between two devices that on the same local LAN shouldn't go through the relay server(A -> LAN router -> relay server ->LAN router ->B), it should just go through the LAN router(A -> LAN router -> B),however the tailscale works well. **Are you using NetBird Cloud?** No , I'm self-hosted Please specify whether you use NetBird Cloud or self-host NetBird's control plane. **NetBird version** `netbird version`:client 0.60.4 / server management 0.60.7 **Is any other VPN software installed?**: Yes If yes, which one? :tailscale (when running netbird,I have disabled tailscale) **Debug output** To help us resolve the problem, please attach the following anonymized status output netbird status -dA output: ``` Peers detail: vivo.netbird.selfhosted: NetBird IP: 100.100.0.34 Public key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Status: Connecting -- detail -- Connection type: - ICE candidate (Local/Remote): -/- ICE candidate endpoints (Local/Remote): -/- Relay server address: Last connection update: 6 minutes, 36 seconds ago Last WireGuard handshake: - Transfer status (received/sent) 0 B/0 B Quantum resistance: false Networks: - Latency: 0s lenovo-notebook.netbird.selfhosted: NetBird IP: 100.100.0.60 Public key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Status: Connecting -- detail -- Connection type: - ICE candidate (Local/Remote): -/- ICE candidate endpoints (Local/Remote): -/- Relay server address: Last connection update: 6 minutes, 36 seconds ago Last WireGuard handshake: - Transfer status (received/sent) 0 B/0 B Quantum resistance: false Networks: - Latency: 0s nas.netbird.selfhosted: NetBird IP: 100.100.0.177 Public key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Status: Connected -- detail -- Connection type: Relayed ICE candidate (Local/Remote): -/- ICE candidate endpoints (Local/Remote): -/- Relay server address: rels://netbird.<mydomain>:61143/relay Last connection update: 6 minutes, 36 seconds ago Last WireGuard handshake: 29 seconds ago Transfer status (received/sent) 3.4 MiB/1.2 MiB Quantum resistance: false Networks: 192.168.11.0/24 Latency: 0s office.netbird.selfhosted: NetBird IP: 100.100.0.187 Public key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Status: Connected -- detail -- Connection type: Relayed ICE candidate (Local/Remote): -/- ICE candidate endpoints (Local/Remote): -/- Relay server address: rels://netbird.<mydomain>:61143/relay Last connection update: 6 minutes, 12 seconds ago Last WireGuard handshake: 1 minute, 58 seconds ago Transfer status (received/sent) 924 B/808 B Quantum resistance: false Networks: 172.16.0.0/16, 192.168.1.0/24 Latency: 0s Events: [INFO] SYSTEM (c7fd4fa1-a886-4f02-96b5-e547558d826a) Message: Network map updated Time: 2 hours, 48 minutes ago [INFO] SYSTEM (a2186d03-be31-44fb-8370-5062cf610c32) Message: Network map updated Time: 2 hours, 32 minutes ago [INFO] SYSTEM (323f0fd8-ddf8-4307-ab3f-25e9fc4f12b5) Message: Network map updated Time: 47 minutes, 57 seconds ago [INFO] SYSTEM (3c212068-2bd6-4793-81d8-e12c6f47ffb9) Message: Network map updated Time: 45 minutes, 38 seconds ago [INFO] SYSTEM (bce1262e-e9af-4e5d-ba13-b898e938d337) Message: Network map updated Time: 35 minutes, 27 seconds ago [INFO] SYSTEM (8b724771-b7c5-4edb-a858-8ae78645fc1d) Message: Network map updated Time: 20 minutes, 52 seconds ago [INFO] SYSTEM (10ae7f24-ba3a-4182-a6cb-b34d4b168dd2) Message: Network map updated Time: 18 minutes, 59 seconds ago [INFO] SYSTEM (04db39f6-9c78-4037-bddb-4267c3b5aa8c) Message: Network map updated Time: 18 minutes, 45 seconds ago [INFO] SYSTEM (8a120eb1-e42c-4eb0-afc3-80f62873a4dd) Message: Network map updated Time: 7 minutes, 1 seconds ago [INFO] SYSTEM (d4700ab2-fdb4-4483-b2f3-63e7d6c1f7b8) Message: Network map updated Time: 6 minutes, 36 seconds ago OS: windows/amd64 Daemon version: 0.60.4 CLI version: 0.60.4 Profile: default Management: Connected to https://netbird.<mydomain>:61143 Signal: Connected to https://netbird.<mydomain>:61143 Relays: [stun:netbird.<mydomain>:3478] is Available [turn:netbird.<mydomain>:3478?transport=udp] is Available [rels://netbird.<mydomain>:61143/relay] is Available Nameservers: FQDN: win-pc.netbird.selfhosted NetBird IP: 100.100.0.155/24 Interface type: Userspace Quantum resistance: false Lazy connection: false SSH Server: Disabled Networks: - Forwarding rules: 0 Peers count: 2/4 Connected ``` Create and upload a debug bundle, and share the returned file key: netbird debug for 1m -AS -U ``` ac37d4641684b3657e68ca9c991147161e6873087035defacb573705b470b899/6a549cab-01c7-4400-aa8c-f67d70ac5e07 ``` *Uploaded files are automatically deleted after 30 days.* Alternatively, create the file only and attach it here manually: netbird debug for 1m -AS **Screenshots** If applicable, add screenshots to help explain your problem. **Additional context** Add any other context about the problem here. **Have you tried these troubleshooting steps?** - [x] Reviewed [client troubleshooting](https://docs.netbird.io/how-to/troubleshooting-client) (if applicable) - [x] Checked for newer NetBird versions - [x] Searched for similar issues on GitHub (including closed ones) - [x] Restarted the NetBird client - [x] Disabled other VPN software - [x] Checked firewall settings
saavagebueno added the triage-needed label 2026-08-05 01:25:51 -04:00
Author
Owner

@1nerdyguy commented on GitHub (Dec 15, 2025):

Look at your routes on the windows client. I promise you you have 2 routes for your local network, 1 which uses netbird as the next hop, and one that doesn't. I also nearly promise you the metric of the netbird one is lower, meaning Windows will use it.

You can fix this, sorta, by adding a policy to the network object saying dont accept connections from that same network range.

<!-- gh-comment-id:3656437095 --> @1nerdyguy commented on GitHub (Dec 15, 2025): Look at your routes on the windows client. I promise you you have 2 routes for your local network, 1 which uses netbird as the next hop, and one that doesn't. I also nearly promise you the metric of the netbird one is lower, meaning Windows will use it. You can fix this, sorta, by adding a policy to the network object saying dont accept connections from that same network range.
Author
Owner

@cheetah747 commented on GitHub (Dec 15, 2025):

Look at your routes on the windows client. I promise you you have 2 routes for your local network, 1 which uses netbird as the next hop, and one that doesn't. I also nearly promise you the metric of the netbird one is lower, meaning Windows will use it.

You can fix this, sorta, by adding a policy to the network object saying dont accept connections from that same network range.

route print:

IP mask gateway metric
192.168.11.0 255.255.255.0 192.168.11.86 281
192.168.11.0 255.255.255.0 100.100.0.155 6

As you said, there are two local routes. I tried manually changing the NetBird route metric from 6 to 500, and it does prevent traffic from going through the relay. However, I think this is a pretty poor user experience: every time NetBird disconnects and reconnects, the wt0 network adapter resets its metric back to 6, which means I have to manually change it again on every connection.

When you mentioned “using policies,” were you referring to the Peer Network Range feature in Create Posture Check? I hadn’t noticed this feature before — thanks for pointing it out. I’ll give it a try.

<!-- gh-comment-id:3658605568 --> @cheetah747 commented on GitHub (Dec 15, 2025): > Look at your routes on the windows client. I promise you you have 2 routes for your local network, 1 which uses netbird as the next hop, and one that doesn't. I also nearly promise you the metric of the netbird one is lower, meaning Windows will use it. > > You can fix this, sorta, by adding a policy to the network object saying dont accept connections from that same network range. route print: | IP | mask | gateway | metric | | -| - | - | - | | 192.168.11.0 | 255.255.255.0 | 192.168.11.86 | 281 | | 192.168.11.0 | 255.255.255.0 | 100.100.0.155 | 6 | As you said, there are two local routes. I tried manually changing the NetBird route metric from 6 to 500, and it does prevent traffic from going through the relay. However, I think this is a pretty poor user experience: every time NetBird disconnects and reconnects, the wt0 network adapter resets its metric back to 6, which means I have to manually change it again on every connection. When you mentioned “using policies,” were you referring to the Peer Network Range feature in Create Posture Check? I hadn’t noticed this feature before — thanks for pointing it out. I’ll give it a try.
Author
Owner

@1nerdyguy commented on GitHub (Dec 16, 2025):

The poor part of this is microsoft randomly adding 256 or more to a local route for 'reasons'.

I have not had a direct overlap like you have here. In my experience. I was publishing a route for 10.10.1.0/24, while my clients were in a different vlan of 10.10.2.0/24.

If you look in the debug log, you'll see some messages about it detecting the route and stopping.

<!-- gh-comment-id:3660557984 --> @1nerdyguy commented on GitHub (Dec 16, 2025): The poor part of this is microsoft randomly adding 256 or more to a local route for 'reasons'. I have not had a direct overlap like you have here. In my experience. I was publishing a route for 10.10.1.0/24, while my clients were in a different vlan of 10.10.2.0/24. If you look in the debug log, you'll see some messages about it detecting the route and stopping.
Author
Owner

@JamesTX10 commented on GitHub (Jan 21, 2026):

How can this be fixed?

<!-- gh-comment-id:3780791542 --> @JamesTX10 commented on GitHub (Jan 21, 2026): How can this be fixed?
Author
Owner

@InternetWorkAcct commented on GitHub (Apr 27, 2026):

How can this be fixed?

Copying my shitty workaround from #4990 until there's a better solution:
The route that has a metric of 6? Defined as a Netbird resource / network? Change it from a /24 to a /23 (255.255.254.0). This will make it prefer the other route.
Of course, if you have an adjacent /24 network, this will present you with a new problem. Hence why the fix is shitty.

<!-- gh-comment-id:4327334006 --> @InternetWorkAcct commented on GitHub (Apr 27, 2026): > How can this be fixed? Copying my shitty workaround from #4990 until there's a better solution: The route that has a metric of 6? Defined as a Netbird resource / network? Change it from a /24 to a /23 (255.255.254.0). This will make it prefer the other route. Of course, if you have an adjacent /24 network, this will present you with a new problem. Hence why the fix is shitty.
Author
Owner

@Cavekeeper commented on GitHub (Jun 25, 2026):

I ran into this exact issue with a Synology DiskStation (DSM) configured as a routing peer for its own local subnet.

Setup:

  • NetBird Cloud (managed)
  • Routing peer 1: Home Assistant (HAOS, IP 192.168.178.5)
  • Routing peer 2: Synology DS723+ running DSM (IP 192.168.178.10)
  • Network resource: 192.168.178.0/24 (full home LAN)
  • Both routing peers are physically located inside the advertised subnet

What happened:
After adding the DiskStation as a second routing peer for 192.168.178.0/24, outbound connections from the DiskStation itself (Synology QuickConnect and DDNS) stopped working completely. The services became unreachable from the outside.

After removing NetBird from the DiskStation entirely and rebooting, QuickConnect and DDNS immediately recovered.

Root cause (suspected):
NetBird injected a route for 192.168.178.0/24 via the WireGuard interface on the DiskStation, conflicting with the existing local route for the same subnet on eth0. DSM's routing table then had duplicate routes for the same network, causing outbound traffic (QuickConnect, DDNS) to be misrouted.

Tailscale comparison:
Tailscale handles this gracefully by detecting that the advertising peer is already inside the advertised subnet and avoids injecting a conflicting local route. NetBird does not appear to have this safeguard.

Workaround:
Removed the DiskStation as a routing peer. Home Assistant alone now handles routing for 192.168.178.0/24, and the DiskStation (192.168.178.10/32) is reachable as a resource through HA as the single routing peer. No more conflicts.

Suggestion:
NetBird should detect when a routing peer is configured to advertise its own local subnet and either warn the user, skip injecting the conflicting route locally, or handle it the way Tailscale does.

<!-- gh-comment-id:4803489820 --> @Cavekeeper commented on GitHub (Jun 25, 2026): I ran into this exact issue with a Synology DiskStation (DSM) configured as a routing peer for its own local subnet. **Setup:** - NetBird Cloud (managed) - Routing peer 1: Home Assistant (HAOS, IP 192.168.178.5) - Routing peer 2: Synology DS723+ running DSM (IP 192.168.178.10) - Network resource: 192.168.178.0/24 (full home LAN) - Both routing peers are physically located inside the advertised subnet **What happened:** After adding the DiskStation as a second routing peer for 192.168.178.0/24, outbound connections from the DiskStation itself (Synology QuickConnect and DDNS) stopped working completely. The services became unreachable from the outside. After removing NetBird from the DiskStation entirely and rebooting, QuickConnect and DDNS immediately recovered. **Root cause (suspected):** NetBird injected a route for 192.168.178.0/24 via the WireGuard interface on the DiskStation, conflicting with the existing local route for the same subnet on eth0. DSM's routing table then had duplicate routes for the same network, causing outbound traffic (QuickConnect, DDNS) to be misrouted. **Tailscale comparison:** Tailscale handles this gracefully by detecting that the advertising peer is already inside the advertised subnet and avoids injecting a conflicting local route. NetBird does not appear to have this safeguard. **Workaround:** Removed the DiskStation as a routing peer. Home Assistant alone now handles routing for 192.168.178.0/24, and the DiskStation (192.168.178.10/32) is reachable as a resource through HA as the single routing peer. No more conflicts. **Suggestion:** NetBird should detect when a routing peer is configured to advertise its own local subnet and either warn the user, skip injecting the conflicting route locally, or handle it the way Tailscale does.
Author
Owner

@rubencastello1990 commented on GitHub (Jul 7, 2026):

Any update on this? having same issue.

<!-- gh-comment-id:4902164336 --> @rubencastello1990 commented on GitHub (Jul 7, 2026): Any update on this? having same issue.
Author
Owner

@Optic00 commented on GitHub (Jul 17, 2026):

I am hitting exactly the symptom described here: a subnet route whose prefix contains a peer's own LAN sends local traffic through WireGuard (or a relay) instead of the native link, which badly hurts throughput on fast local networks (2.5G/5G).
Deselecting the route per device does not scale to roaming machines (a laptop moved between sites, a phone), and the network-change script that would automate it cannot run on iOS at all, so the logic has to live in the client.
I would like to propose a design for discussion before any code.

Relation to recent work

Discussion #6138 and PR #6142 (merged) already solved the peer-level half: dropping the signaling-side candidateViaRoutes filter lets two NetBird peers on the same LAN connect directly via host candidates even when that LAN is advertised as a network route.
This proposal is the routing-level complement: traffic to the subnet as a whole (non-NetBird hosts such as a NAS, printers, or controllers reached via a routing peer) still detours through the tunnel when the client sits inside that subnet, which is the gap this issue and #4990 describe.
The workarounds posted here (advertising a broader prefix so the local route wins on metric) are fragile and depend on platform-specific metric behavior, so an in-client mechanism seems worth designing.

Proposal: local-route preference with an authenticated trigger

Mechanism

  • When the client has a directly connected interface whose subnet overlaps a pushed route, traffic for that local subnet uses the native link instead of the tunnel.
  • This is not "deselect the whole route". For an aggregate route (a pushed 192.168.0.0/16 overlapping a local /24), the tunnel route stays installed and only the locally connected subprefix gets precedence as a local exception.
  • Overlap detection is symmetric (route covers the local subnet, or the local subnet covers the route), not just base-address containment.
  • The state is transient and event-driven, re-evaluated on network change, so leaving the site restores normal behavior automatically.

Security trigger: authenticated site presence

Triggering on "I have a local IP in the subnet" alone is spoofable, because a hostile LAN handing out the same RFC1918 range could redirect the client to an impostor host.
So the bypass would also require an authenticated witness of site presence: the client must reach the route's designated routing peer natively, over a host/host ICE candidate pair from inside that subnet.
Reaching the authorized routing peer over the local link is hard to spoof without that peer's WireGuard key, and it reuses the host-candidate path that PR #6142 just made reliable on-LAN, so it fits naturally.
If that signal is absent or lost, the client restores the tunnel path immediately (fail closed).

Policy and safety

  • Opt-in and default off, controlled per route or network from management.
  • Native local access bypasses NetBird's route firewall, ACL enforcement, and encryption, and routes already carry AccessControlGroups, so ACL-controlled routes, exit routes, and anything deliberately meant to stay tunnelled are never bypassed.
  • A force-tunnel choice always wins over the local preference.
  • Platform note: Linux with advanced routing already tends to prefer local main-table routes, so the practical gap is mainly Windows, macOS, and iOS, and the decision belongs in the route selection layer so mobile inherits it through the existing notifier plumbing.

Suggested v1 scope

Static IPv4/IPv6 prefix routes only, no exit nodes, no DNS routes, default off, per-route opt-in.

Related: #5278 (trusted-network auto-disable) and #4990 (route-metric relaying).
Unlike an auto netbird down, this deliberately preserves peer tunnels and unrelated remote routes.

@lixmal since you merged PR #6142: does this routing-level complement fit the direction taken there?
Before any code I would appreciate guidance on two points, in the spirit of CONTRIBUTING's "align with the team first": the per-route policy surface in management, and whether the authenticated site-presence witness is an acceptable trigger.
I am glad to help design and prototype v1.

<!-- gh-comment-id:5001534332 --> @Optic00 commented on GitHub (Jul 17, 2026): I am hitting exactly the symptom described here: a subnet route whose prefix contains a peer's own LAN sends local traffic through WireGuard (or a relay) instead of the native link, which badly hurts throughput on fast local networks (2.5G/5G). Deselecting the route per device does not scale to roaming machines (a laptop moved between sites, a phone), and the network-change script that would automate it cannot run on iOS at all, so the logic has to live in the client. I would like to propose a design for discussion before any code. ## Relation to recent work Discussion #6138 and PR #6142 (merged) already solved the peer-level half: dropping the signaling-side `candidateViaRoutes` filter lets two NetBird peers on the same LAN connect directly via host candidates even when that LAN is advertised as a network route. This proposal is the routing-level complement: traffic to the subnet as a whole (non-NetBird hosts such as a NAS, printers, or controllers reached via a routing peer) still detours through the tunnel when the client sits inside that subnet, which is the gap this issue and #4990 describe. The workarounds posted here (advertising a broader prefix so the local route wins on metric) are fragile and depend on platform-specific metric behavior, so an in-client mechanism seems worth designing. ## Proposal: local-route preference with an authenticated trigger ### Mechanism - When the client has a directly connected interface whose subnet overlaps a pushed route, traffic for that local subnet uses the native link instead of the tunnel. - This is not "deselect the whole route". For an aggregate route (a pushed `192.168.0.0/16` overlapping a local `/24`), the tunnel route stays installed and only the locally connected subprefix gets precedence as a local exception. - Overlap detection is symmetric (route covers the local subnet, or the local subnet covers the route), not just base-address containment. - The state is transient and event-driven, re-evaluated on network change, so leaving the site restores normal behavior automatically. ### Security trigger: authenticated site presence Triggering on "I have a local IP in the subnet" alone is spoofable, because a hostile LAN handing out the same RFC1918 range could redirect the client to an impostor host. So the bypass would also require an authenticated witness of site presence: the client must reach the route's designated routing peer natively, over a host/host ICE candidate pair from inside that subnet. Reaching the authorized routing peer over the local link is hard to spoof without that peer's WireGuard key, and it reuses the host-candidate path that PR #6142 just made reliable on-LAN, so it fits naturally. If that signal is absent or lost, the client restores the tunnel path immediately (fail closed). ### Policy and safety - Opt-in and default off, controlled per route or network from management. - Native local access bypasses NetBird's route firewall, ACL enforcement, and encryption, and routes already carry `AccessControlGroups`, so ACL-controlled routes, exit routes, and anything deliberately meant to stay tunnelled are never bypassed. - A force-tunnel choice always wins over the local preference. - Platform note: Linux with advanced routing already tends to prefer local main-table routes, so the practical gap is mainly Windows, macOS, and iOS, and the decision belongs in the route selection layer so mobile inherits it through the existing notifier plumbing. ### Suggested v1 scope Static IPv4/IPv6 prefix routes only, no exit nodes, no DNS routes, default off, per-route opt-in. ## Related and coordination Related: #5278 (trusted-network auto-disable) and #4990 (route-metric relaying). Unlike an auto `netbird down`, this deliberately preserves peer tunnels and unrelated remote routes. @lixmal since you merged PR #6142: does this routing-level complement fit the direction taken there? Before any code I would appreciate guidance on two points, in the spirit of CONTRIBUTING's "align with the team first": the per-route policy surface in management, and whether the authenticated site-presence witness is an acceptable trigger. I am glad to help design and prototype v1.
Sign in to join this conversation.
No Label triage-needed
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#10419