One machine, two jobs, one firewall: a Wi-Fi uplink, a small Ethernet subnet behind it, and a desktop VPN client that assumes everything leaving the box is local. Locally launched applications leave through the local output path. Devices behind the workstation arrive on the Ethernet port and need a forwarding decision. An exclusion written for the first path never sees the second, and that is why a browser bypasses the tunnel while the devices do not.

Two packet paths, one policy

OriginPathDecision owner
Application on the workstationOUTPUT then egressThe VPN app's routing policy
Device behind the workstationPREROUTING then FORWARDThe workstation's own forwarding policy

The first row is what most split-tunnelling guides describe. The second is a router configuration that happens to live on the same machine. Merging them into one mental model is the mistake; they share a kernel but not a policy. Draw both paths before editing any rule, and the rest of the design follows from which path is missing.

The table also explains why one machine can pass one test and fail another. A browser on the workstation proves the local path; a laptop plugged into the Ethernet port proves the forwarding path. Validate a change with each, or the second one stays untested however many times the browser is refreshed.

What an application exclusion actually classifies

A per-application exclusion usually works by marking packets from a process or a user, then routing marked traffic outside the tunnel. That is a local-output construct. A packet from a downstream device has no local process, so it never matches the selector, whatever the selector is.

Whatever the selector is, the two lists are not the same list, and that is the whole point. The Mullvad Linux guide describes marks consumed by its firewall and routing policy, and its examples target local output; they are not a supported description of a downstream router. Copying them yields a machine that believes it has an exclusion while forwarding everything into the tunnel.

Write the transit contract

Name the downstream source prefix and the ingress interface together, then restrict that prefix at the ingress. Without the restriction, any device that can reach the port can claim an excluded address and leave through the bypass. Treat it as two questions: may these hosts use this uplink at all, and may they reach the workstation or the tunnel's private ranges? Those are additional permissions, not side effects of the uplink choice.

Permit forwarding, then filter it. The Linux gateway needs forwarding enabled, rules that allow the intended traffic in both directions, and either an upstream return route or deliberately scoped source translation. NAT alone does not select the uplink and does not grant passage through a filter; it only rewrites an address after those decisions.

Classification for transit has to happen before the forwarding route decision, which means the prerouting stage rather than output. A rule placed after the decision can still change the packet, but it cannot change where the packet was already sent.

Marks belong to the application that owns them

Mark values, table numbers, and chain priorities are part of the VPN application's policy, not timeless constants. A durable network that depends on inserting a rule into a table another program regenerates will break on the next update, usually during a reconnect rather than at a convenient time.

Adding a second base chain with an accept verdict does not necessarily override a later chain's drop. Base chains have hooks and priorities, and the first verdict in the ordering wins; an accept in one chain does not cancel a drop that another chain reaches first. Look at the complete ruleset and the routing policy order, not at the one counter that incremented. A counter proves a rule ran, never that its verdict was the one applied.

Integrate, or move the subnet

The two designs differ in who owns the bypass. An integrated design keeps one machine and one policy, at the cost of depending on the VPN application's table lifecycle and on a set of marks that its documentation may change. A dedicated router for the downstream network removes that dependency entirely: the subnet has its own uplink, and the workstation remains a client with no forwarding role.

Choose the second when the downstream network must keep working through VPN updates, or when nobody can test the integrated policy after each release. Choose the first only when the integration is documented and someone owns it.

Verify the boundary in four states: connected, reconnecting, deliberately blocked, and stopped. Check name resolution and both address families under each, because a bypass that leaks DNS is not the exclusion you designed. The test should prove the precise boundary, not just that one page loaded.

Qillqakuna t'inkisqa