diff --git a/src/pages/manage/network-routes/use-cases/by-scenario/site-to-vpn.mdx b/src/pages/manage/network-routes/use-cases/by-scenario/site-to-vpn.mdx
index e39fdd15..b6542175 100644
--- a/src/pages/manage/network-routes/use-cases/by-scenario/site-to-vpn.mdx
+++ b/src/pages/manage/network-routes/use-cases/by-scenario/site-to-vpn.mdx
@@ -23,19 +23,36 @@ Clientless Device ──► Routing Peer ──► NetBird Overlay ──► Net
```
-Site-to-VPN is implemented with [Network Routes](/manage/network-routes). The newer [Networks](/manage/networks) feature does not yet expose
-the routed CIDR as a policy source, which is required for this scenario.
+This scenario works with both [Networks](/manage/networks) (recommended) and
+[Network Routes](/manage/network-routes) — the configuration is the same in
+substance, only the dashboard surface differs. Use Networks unless you
+already have an established Network Routes setup. This guide uses Networks.
-With Network Routes, the overlay peer will always observe the **routing
-peer's NetBird IP** as the source of the request, not the clientless
-device's local IP. See [Source IP Behavior](#source-ip-behavior) below for
-the details and why this is the case.
+The routing peer must perform **outbound source NAT** for site traffic
+entering the NetBird overlay. NetBird performs this automatically only when
+the routing peer is **Linux running in kernel mode**. On any other platform
+(pfSense, OPNsense, MikroTik, Windows, macOS, or Linux in userspace mode),
+you must configure the outbound NAT yourself on the routing peer or on its
+upstream firewall. Without this, the overlay peer drops the traffic at its
+access control. See [Outbound SNAT requirement](#outbound-snat-requirement).
+
+
+
+The overlay peer will always observe the **routing peer's NetBird IP** as
+the source of the request, not the clientless device's local IP. See
+[Source IP Behavior](#source-ip-behavior) below for the details.
## Prerequisites
+- A [NetBird cloud account](https://app.netbird.io/) or [self-hosted instance](/selfhosted/selfhosted-quickstart)
+- A device on the local network to serve as the routing peer. **Linux is
+ strongly recommended** for the routing peer because it can install the
+ required outbound SNAT automatically (see the warning above).
+- A separate device running the NetBird client that the clientless device
+ needs to reach
- The ability to either add a static route on the clientless device (or its
upstream router), or to install a port-forwarding rule on the routing peer
— both options are covered below
@@ -77,44 +94,89 @@ sudo netbird up --setup-key YOUR_SITE_SETUP_KEY
Confirm the peer appears in the dashboard and shows the `site-routing-peers`
group.
-## Step 3: Allow Forwarding Through a Host Firewall (if applicable)
+## Step 3: Configure the Outbound SNAT
-If you run a host firewall (UFW, firewalld, custom iptables policy) on the
-routing peer with the `FORWARD` chain default set to `DROP`, allow
-forwarding between the site-facing interface and `wt0`. For example with
-`iptables`:
+This is the most commonly missed step. The routing peer must SNAT site
+traffic onto its NetBird interface so the overlay peer's access control sees
+a NetBird IP it recognises. NetBird does not install this for you reliably
+outside of Linux kernel mode — see
+[Outbound SNAT requirement](#outbound-snat-requirement) for the reasoning.
+
+**On a Linux routing peer (any mode):**
+
+```bash
+sudo sysctl -w net.ipv4.ip_forward=1
+echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-netbird-routing.conf
+
+sudo iptables -t nat -A POSTROUTING -s 192.168.50.0/24 -o wt0 -j MASQUERADE
+# Persist with iptables-persistent / netfilter-persistent or your distro's mechanism
+```
+
+If you run a host firewall (UFW, firewalld) with the `FORWARD` chain default
+set to `DROP`, also allow forwarding between the site-facing interface and
+`wt0`:
```bash
sudo iptables -I FORWARD 1 -i -o wt0 -j ACCEPT
```
-If no host firewall is in the way, no extra rules are needed — NetBird
-handles IP forwarding and source NAT on the routing peer.
+**On pfSense / OPNsense:** add an outbound NAT rule on the `wt0` (or
+equivalent) interface that translates traffic sourced from `192.168.50.0/24`
+to the interface address. Switch outbound NAT mode to **Manual** (or
+**Hybrid**) so this rule is honoured.
-## Step 4: Create the Network Route
+**On MikroTik (RouterOS):**
+
+```
+/ip firewall nat add chain=srcnat src-address=192.168.50.0/24 \
+ out-interface=wt0 action=masquerade
+```
+
+**On other platforms:** any outbound source NAT mechanism that rewrites the
+site-CIDR source to the routing peer's NetBird IP (or to the `wt0`
+interface address) on the NetBird egress path is sufficient.
+
+## Step 4: Create the Network
In the NetBird dashboard:
-1. Go to **Network Routes** and click **Add Route**
-2. Configure:
- - **Network Identifier:** `site-50`
- - **Network range:** `192.168.50.0/24`
- - **Routing Peer:** `site-router` (or use the `site-routing-peers` group)
- - **Distribution Groups:** `backup-collectors` (the group containing the
- peers the clientless device will reach)
- - **Masquerade:** **Enabled** (required — see
- [Source IP Behavior](#source-ip-behavior))
- - **Access Control Groups:** leave empty for site-to-VPN (these only
- apply to the VPN-to-Site direction)
-3. Click **Add Route**
+1. Go to **Networks** and click **Add Network**
+2. Name: `site-50-network`
+3. Click **Create Network**
-The route advertises the site's CIDR onto the overlay, which gives the
-target peer a return path for any traffic that originates from the site.
+Now add the site CIDR as a resource:
+
+1. In the new network, click **Add Resource**
+2. Configure:
+ - **Name:** `site-50`
+ - **Address:** `192.168.50.0/24`
+ - **Type:** Subnet
+ - **Groups:** create and add `site-50-cidr` (this group represents the
+ site CIDR for use in policies)
+3. Click **Add Resource**
+
+Attach the routing peer:
+
+1. In the network, click **Add Routing Peer**
+2. Select `site-router` (or the `site-routing-peers` group)
+3. **Masquerade:** Enabled is fine, but does not replace
+ [Step 3](#step-3-configure-the-outbound-snat) — this dashboard flag does
+ not install a working SNAT on non-Linux routing peers and is unreliable in
+ userspace mode
+4. Click **Save**
+
+
+If you prefer Network Routes, the equivalent is **Network Routes → Add
+Route** with: network range `192.168.50.0/24`, routing peer `site-router`,
+distribution group `backup-collectors`, masquerade enabled. The rest of this
+guide applies unchanged.
+
## Step 5: Create the Access Policy
The routing peer needs to be allowed to reach the target peer over the
-overlay:
+overlay. Because Step 3's SNAT rewrites the source to the routing peer's
+NetBird IP, the policy uses peer groups:
1. Go to **Access Control** → **Policies** and click **Add Policy**
2. Configure:
@@ -125,10 +187,6 @@ overlay:
- **Destination Groups:** `backup-collectors`
3. Click **Add Policy**
-Because the route has masquerade enabled, traffic from the clientless
-device will appear on the overlay as originating from the routing peer's
-NetBird IP. The policy authorises that flow.
-
## Step 6: Direct Site Traffic Through the Routing Peer
You have two options for getting the clientless device's traffic to the
@@ -209,7 +267,6 @@ curl -v http://192.168.50.10:18080/
Verify on the target peer that the request arrived:
```bash
-# Replace with whatever the target peer is listening with
sudo ss -tnp | grep :8080
```
@@ -255,34 +312,45 @@ the `server=/netbird.cloud/...` line — you can find it with
`netbird status` on the routing peer.
+## Outbound SNAT requirement
+
+Why is [Step 3](#step-3-configure-the-outbound-snat) necessary, and why
+isn't the dashboard's masquerade toggle enough?
+
+NetBird's per-peer access control on the destination peer matches inbound
+traffic against an ipset of allowed source IPs. The ipset is populated from
+the **NetBird IPs of peers in the policy's source group** — it cannot
+contain a routed CIDR like `192.168.50.0/24`. So when a packet from
+`192.168.50.20` arrives at the overlay peer, the access control has no
+matching entry and the packet is dropped.
+
+The fix is to rewrite the source IP at the routing peer before the packet
+enters the overlay, replacing the site IP with the routing peer's NetBird
+IP. That NetBird IP **is** in the policy's source group, so the access
+control matches and the packet is accepted.
+
+On a Linux routing peer in kernel mode, NetBird's daemon can install this
+SNAT itself via the kernel netfilter hooks when masquerade is enabled on
+the route/router. **In every other case** — userspace mode, non-Linux
+peers, or environments where NetBird's marks aren't being set on outbound
+packets — the SNAT must be configured manually on the routing peer (or on
+its upstream firewall), as shown in Step 3. This is a real, observable
+limitation rather than a quirk: without the SNAT, the dashboard masquerade
+flag has no effect on the wire.
+
## Source IP Behavior
-With Network Routes, the target peer always sees the **routing peer's
-NetBird IP** as the source — regardless of which clientless device on the
-site initiated the connection. Two routes lead to this:
+Because of the outbound SNAT above, the target peer always observes the
+**routing peer's NetBird IP** as the source — regardless of which clientless
+device on the site initiated the connection.
-- With **masquerade enabled** on the route, NetBird installs a MASQUERADE
- rule on the routing peer's outbound wireguard interface, rewriting the
- source IP at the routing peer.
-- With **port forwarding (Option B)**, the routing peer SNATs the DNAT'd
- flow so that return traffic comes back to the routing peer.
-
-You **cannot** preserve the clientless device's local IP all the way to
-the target peer through Network Routes. The target peer's per-peer access
-control list matches sources by NetBird IP, and there is no field on a
-legacy Network Route that registers the site CIDR as a policy source.
-Disabling masquerade does not help here: the target peer will receive
-packets with the site source IP, but they will be dropped by its access
-control because that IP is not in any allowed source group.
-
-Preserving the original source IP is **not possible** with legacy Network
-Routes. Removing all policies that target the destination peer does not
-"open" it — it simply tears down the wireguard pairing with the routing
-peer, and the connection fails to establish at all. The newer
-[Networks](/manage/networks) feature is built around CIDR-bearing Resource
-groups and is the future path for preserved-source scenarios; the Site-to-VPN
-direction is not yet supported there. Track the
-[Networks documentation](/manage/networks) for updates.
+Preserving the original site source IP all the way to the target peer is
+**not currently possible**. Removing the access policy that targets the
+destination peer does not "open" it; it simply tears down the wireguard
+pairing between the routing peer and the destination, and the connection
+fails to establish at all. Future versions of [Networks](/manage/networks)
+may expose Resource groups as policy sources for the Site-to-VPN direction
+— track the Networks documentation for updates.
## Troubleshooting
@@ -316,17 +384,22 @@ allows `site-routing-peers` → target peer's group on the required port.
**Target peer receives packets but drops them.**
-If you disabled masquerade on the routing peer, the target peer will see
-the raw site source IP and drop the packets at its per-peer access
-control. Enable masquerade on the routing peer to fix this.
+The outbound SNAT in [Step 3](#step-3-configure-the-outbound-snat) is
+missing or not effective. On the routing peer, packets going out `wt0` must
+have their source IP rewritten to the routing peer's NetBird IP:
```bash
-# On the target peer, watch for drops:
-sudo iptables -L INPUT -n -v | grep DROP
-# DROP counter on the wt0 interface increasing means the packet
-# was rejected by access control.
+# On the routing peer, verify packet counters on the MASQUERADE rule:
+sudo iptables -t nat -L POSTROUTING -n -v
+# Or watch overlay traffic on the way out:
+sudo tcpdump -ni wt0 'src net 192.168.50.0/24'
+# Seeing site IPs here means SNAT is NOT firing; the target peer will drop.
```
+If you have the dashboard masquerade flag enabled but counters stay at
+zero, NetBird's mark-driven SNAT isn't installing — fall back to the
+explicit `iptables -t nat -A POSTROUTING` rule from Step 3.
+
**DNS resolution returns NXDOMAIN.**
Confirm the routing peer's NetBird IP is correct in `dnsmasq.conf` (it
@@ -336,7 +409,7 @@ changes if the peer is re-enrolled), and that `dnsmasq` is not bound to
## Related
+- [Networks — Concept](/manage/networks)
- [Network Routes — Concept](/manage/network-routes)
- [Site-to-Site: Office Networks](/manage/network-routes/use-cases/by-scenario/site-to-site-office)
-- [Configuring Routes with Access Control](/manage/network-routes/use-cases/by-configuration/access-control)
- [Site-to-Site Overview](/use-cases/site-to-site)
diff --git a/src/pages/use-cases/site-to-site/index.mdx b/src/pages/use-cases/site-to-site/index.mdx
index 0e27e2b9..5f3e780d 100644
--- a/src/pages/use-cases/site-to-site/index.mdx
+++ b/src/pages/use-cases/site-to-site/index.mdx
@@ -42,7 +42,7 @@ Office Server ──────► Routing Peer ──────► NetBird T
- On-premise servers initiating backups to cloud peers
- Legacy systems that must initiate outbound connections
-**Implementation:** Requires [Network Routes](/manage/network-routes) (Networks does not currently support this)
+**Implementation:** Use [Networks](/manage/networks) (recommended) or [Network Routes](/manage/network-routes). See the [Site-to-VPN guide](/manage/network-routes/use-cases/by-scenario/site-to-vpn).
### Site-to-Site
@@ -82,7 +82,7 @@ Your Laptop ──────► NetBird Tunnel ──────► Exit Node
|-------------|----------|----------------|
| Access home devices from my laptop | VPN-to-Site | [Networks](/manage/networks/use-cases/by-scenario/access-home-devices) |
| Access office resources while traveling | VPN-to-Site | [Networks](/manage/networks/use-cases/by-scenario/remote-worker-access) |
-| Let an office server connect to my laptop | Site-to-VPN | [Network Routes](/manage/network-routes/use-cases/by-scenario/site-to-vpn) only |
+| Let an office server connect to my laptop | Site-to-VPN | [Networks](/manage/networks) or [Network Routes](/manage/network-routes/use-cases/by-scenario/site-to-vpn) |
| Connect two home networks together | Site-to-Site | [Network Routes](/manage/network-routes/use-cases/by-scenario/site-to-site-home) only |
| Link branch offices | Site-to-Site | [Network Routes](/manage/network-routes/use-cases/by-scenario/site-to-site-office) only |
| Bridge cloud VPC with on-premise network | Site-to-Site | [Network Routes](/manage/network-routes/use-cases/by-scenario/site-to-site-cloud) only |
@@ -158,14 +158,14 @@ NetBird offers two features for routing traffic to private networks: [Networks](
**Use Networks** for VPN-to-Site scenarios where you want a guided setup experience and per-resource access policies.
-**Use Network Routes** when you need Site-to-VPN or Site-to-Site connectivity, or require advanced options like disabling masquerade.
+**Use Network Routes** when you need Site-to-Site connectivity, or require advanced options like disabling masquerade.
### Scenario Support
| Scenario | Networks | Network Routes |
|----------|----------|----------------|
| VPN-to-Site | Yes | Yes |
-| Site-to-VPN | No | Yes |
+| Site-to-VPN | Yes | Yes |
| Site-to-Site | No | Yes |
### Detailed Comparison