#3474·headscale

[Bug] Bug: Inaccessible routes are being advertised/pushed to clients after update

Author: kgncengizCreated Sep 12, 2026Updated Sep 14, 2026
Labelsbugregression

Is this a support request?

  • This is not a support request

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

After updating Headscale, I observed a regression in how advertised routes are distributed. If Node A advertises multiple routes or subnets, Node B receives all advertised routes from Node A, even if ACL rules explicitly restrict Node B from accessing specific subnets. In previous versions, Headscale filtered out routes that the client had no permission to reach. While Node B still needs access to Node A itself (or other services on it), receiving unauthorized subnet routes can cause local routing conflicts on Node B.

Expected Behavior

Inaccessible routes should not be pushed to clients that cannot reach them.

Steps To Reproduce

1- Set up a Headscale network with Node A and Node B. 2- Configure Node A to advertise a subnet route (e.g., 192.168.1.0/24). 3- Configure ACL rules such that Node B can communicate with Node A directly (or access a specific port/service), but is explicitly denied access to the advertised subnet 192.168.1.0/24. 4- Connect both nodes to Headscale and approve the subnet route on Node A. 5- Inspect the routing table on Node B (tailscale status or ip route).

Environment

markdown
- OS: debian 13 trixie
- Headscale version: 0.29.3
- Tailscale version: 1.102.3

Runtime environment

  • Headscale is behind a (reverse) proxy
  • Headscale runs in a container

Debug information

Pushing subnet routes to devices that are explicitly denied access to those subnets via ACL makes no practical sense. It bypasses the expected isolation controls and forces client nodes to accept routing entries for networks they cannot interact with.

In our environment, this change broke system routing and created IP/subnet conflicts on client devices. If this routing behavior was changed intentionally for specific edge cases, it should at least be made configurable via config.yaml (e.g., filter_unauthorized_routes: true) so existing deployments don't suffer from unintended network disruption.