OPNsense Router
Isometric dark server chassis on a pink platform wired to three small glowing router nodes, suggesting inbound traffic spread across multiple WAN links
Multi-WAN

Multi WAN Inbound Load Balance: DNS, BGP or a Proxy

Your router can't steer inbound connections. How to balance them across two WANs on OPNsense with health-checked DNS, a proxy front end, or BGP.

By OPNsense Router Editorial · · 9 min read

There is no router checkbox for a multi wan inbound load balance setup, on OPNsense or anything else: the remote client decides which of your public IPs it connects to, so the balancing has to happen before the packet reaches you. Do it in one of three places: health-checked DNS, a proxy front end with its own public address, or BGP with your own prefix.

OPNsense’s part is narrower: accept the service on every WAN and reply out the WAN each request arrived on. Most homes should pick health-checked DNS or a proxy front end.

Why can’t OPNsense balance inbound connections itself?

Because the sender picks the inbound path. Gateway groups attach route-to to firewall rules, which only steers traffic your own networks originate. An outside client resolves a name, gets an address, and its ISP routes toward that address. By the time your firewall sees the SYN, the client has already picked the circuit.

The OPNsense multi-WAN how-to reflects this: it covers outbound gateway selection only. Server load balancing (HAProxy or relayd spreading one public IP across several internal hosts) is a different problem: one WAN, many backends.

What does OPNsense have to do on every WAN?

Whatever does the balancing in front of it, the firewall has four jobs per WAN: keep reply-to on, add a port forward on each WAN, publish a DNS name for each WAN, and confirm no circuit sits behind CGNAT.

1. Keep reply-to on. The firewall settings reference says: “With Multi-WAN you generally want to ensure traffic leaves the same interface it arrives on.” OPNsense does that by adding reply-to to rules on WAN-type interfaces, so packets reply to the gateway connected to the interface they arrived on. Leave Disable reply-to in Firewall > Settings > Advanced unticked, and make sure each WAN has its gateway assigned in the interface config. Otherwise a SYN that arrives on WAN2 gets its SYN-ACK sent out the default gateway on WAN1 with WAN2’s source address, where it is usually dropped upstream. The rules documentation calls this the “reply-to issue” and notes that reply-to is “only relevant in the context of a state,” so stateless pass rules never get it.

2. One port forward per WAN. In Firewall > NAT > Destination NAT (Port Forward), the Interface field selects where the traffic originates. Create the rule with Interface WAN2 and destination WAN2 address, alongside the existing WAN rule. Set the filter rule association to Register rule, which the docs recommend.

3. One DNS name per WAN. If either circuit has a dynamic address, configure one os-ddclient account per WAN and set “Interface to monitor” to that WAN, so the IP check goes out through it. Publish wan1.home.example.net and wan2.home.example.net, and point health checks and proxy backends at those names.

4. Rule out CGNAT. If a WAN’s address falls inside 100.64.0.0/10, the RFC 6598 shared address space reserved for carrier-grade NAT, unsolicited traffic never reaches you on that circuit. Cellular backup links often look like this. Such a WAN can only take part in inbound traffic through an outbound tunnel to a front end (option 2).

Option 1: multiple A records, ideally health-checked

Publish both WAN addresses under one name:

home.example.net.  60  IN  A  203.0.113.2
home.example.net.  60  IN  A  198.51.100.2

This is the oldest trick there is. RFC 1794, “DNS Support for Load Balancing,” documented it in 1995, and it was already worried about TTLs, describing five minutes as a normal interval. Plain round robin has three limits:

  • No health awareness. A dead WAN’s address keeps being handed out until someone edits the zone.
  • Clients reorder answers. RFC 6724 admits in its appendix that the earlier address-selection rules could override “DNS load balancing by forcing the destination address with the most bits in common to be always chosen.”
  • Recovery depends on the client. Happy Eyeballs v2 (RFC 8305) clients start “one connection attempt to a single address,” then try the others one at a time, with 250 ms as the recommended default delay. A dead WAN costs those clients a quarter of a second. A client that only tries the first address hangs until its own timeout.

Health-checked DNS fixes the first problem. Route 53 multivalue answer routing returns up to eight healthy records and drops any whose health check fails. AWS states plainly that this “isn’t a substitute for a load balancer.” Cloudflare’s DNS-only load balancing adds weights: Random steering takes a weight per pool, so a 1 Gbit fibre can get more traffic than a 100 Mbit VDSL line. Off - Failover uses pool order for an active/standby pair.

Both still depend on resolvers honouring a short TTL. Cloudflare’s proxy modes page warns that “if a client has a cached DNS response, they will go to their previous destination,” and DNS-only mode publishes both of your home IPs.

Mail is the exception. RFC 5321 builds this behaviour into SMTP. Senders sort MX records by preference, must randomise records with equal preference, and must retry each address in turn until delivery succeeds. Two MX records at equal preference, one per WAN, gives you inbound balancing and failover with no health checks at all.

Option 2: a proxy front end with one public address

Hand the decision to a box that has one stable public IP and can health-check both of your circuits. A dead WAN drops out of rotation at the next failed check instead of when a TTL expires.

Hosted: Cloudflare’s proxied mode works at Layer 7 for HTTP and HTTPS, hides your endpoint IPs, and runs monitors from multiple data centres. It is a paid add-on, and proxied mode covers HTTP and HTTPS only, so other protocols need DNS-only mode or a different front end.

Self-hosted: a small VPS running HAProxy, with two WireGuard tunnels back to OPNsense, each pinned to one circuit:

VPS public:  192.0.2.10 (wg A, UDP 51820)   192.0.2.11 (wg B, UDP 51821)
wg0  OPNsense 10.99.1.2/30 <-> VPS 10.99.1.1/30   route 192.0.2.10/32 via WAN_GW
wg1  OPNsense 10.99.2.2/30 <-> VPS 10.99.2.1/30   route 192.0.2.11/32 via WAN2_GW

The two /32 host routes in System > Routes force each tunnel out a specific WAN. OPNsense initiates both tunnels with a persistent keepalive, so this works even if one circuit is behind CGNAT. Assign wg0 and wg1 as interfaces, then port-forward TCP 443 on each one to the internal host. The VPS side:

backend home
    mode tcp
    balance roundrobin
    server via-wan1 10.99.1.2:443 check weight 3
    server via-wan2 10.99.2.2:443 check weight 1

Return traffic needs no reply-to tricks: HAProxy sources each backend connection from its tunnel address, a connected network on that wg interface. There are two costs. The backend sees the VPS as the client unless you add send-proxy-v2 and the service understands PROXY protocol, and the VPS becomes a single point of failure. The tunnel build matches a WireGuard site-to-site setup. If the second WAN’s handshake stalls, work through the handshake, NAT and MTU checks before you suspect HAProxy.

Option 3: BGP and your own prefix

This is the only design where the internet itself routes around a dead circuit. You announce your own prefix to both ISPs. When one session drops, the route is withdrawn and inbound traffic converges on the surviving path.

It takes an ASN, provider-independent address space, and two ISPs willing to run BGP to your edge, which usually means business circuits. RFC 7454 notes that IPv4 prefixes longer than /24 and IPv6 prefixes longer than /48 are “generally neither announced nor accepted.” On OPNsense, the os-frr plugin provides BGPv4: set your AS number under Routing > BGP, add one neighbor per ISP with its remote AS, and list your prefix under Networks. Take default routes rather than full tables unless the box has the RAM; check it against the OPNsense hardware sizing guide. You can influence how inbound traffic splits between the two ISPs, but you can’t set a ratio.

Which option fits your setup?

For most homes, use a proxy front end for HTTPS services and health-checked DNS for everything else. For inbound mail, use two MX records. Choose BGP only if you already have an ASN, provider-independent space, and an uptime commitment that justifies business circuits.

ProfilePickWhy
Home lab, HTTPS servicesCloudflare proxied LB, or VPS + HAProxyFast failover, home IPs hidden
Home, non-HTTP TCP (SSH, game server)VPS + HAProxy in TCP modeOne stable address, per-connection health checks
Static IPs, low change rateRoute 53 multivalue or Cloudflare DNS-onlyCheapest, no extra hop
Small office receiving mailTwo equal-preference MX recordsBuilt into SMTP
One WAN behind CGNATVPS tunnelOnly design that reaches that circuit
Business with an uptime SLABGP via os-frrNo dependency on DNS caches

How do you test it?

Test each WAN on its own from outside your network with curl --resolve, then pull a cable and watch what happens. Use a phone hotspot or the VPS: from the LAN, NAT reflection sends you to the internal address and hides the WAN path entirely.

dig +noall +answer A home.example.net
curl -so /dev/null -w '%{http_code}\n' --resolve home.example.net:443:203.0.113.2 https://home.example.net/
curl -so /dev/null -w '%{http_code}\n' --resolve home.example.net:443:198.51.100.2 https://home.example.net/

Both curl lines must return a status code. If the WAN2 line times out, check reply-to first. On the firewall shell:

pfctl -sr | grep reply-to
tcpdump -ni igb1 'tcp port 443 and tcp[tcpflags] & tcp-syn != 0'

The SYN and the SYN-ACK should both appear on igb1. Then unplug WAN1. Time how long dig keeps returning its address, confirm the HAProxy stats or Cloudflare monitor mark it down, and reload the service in a browser. Plug WAN1 back in and confirm it rejoins the rotation.

FAQ

can you load balance incoming traffic on dual wan

Yes, but not on the router. Incoming connections arrive on whichever public IP the client chose, so the balancing has to happen in DNS, in a proxy front end, or through BGP. The router’s job is to accept the service on both WANs and send each reply out the interface the request arrived on.

does round robin dns provide failover

Only partially. Plain round-robin DNS keeps handing out a dead WAN’s address until the zone is edited, and resolvers serve cached answers until the TTL expires. Clients that implement Happy Eyeballs try the next address after about 250 ms, which hides the failure from them. Add health checks through Route 53 or Cloudflare to pull dead records.

why does my port forward not work on wan2

Usually because the replies leave through WAN1. OPNsense adds reply-to to rules on WAN-type interfaces so traffic exits where it entered. That only works if Disable reply-to is unticked, WAN2 has a gateway assigned, and the rule is stateful. Also confirm the port forward’s interface is WAN2, not WAN.

do i need bgp for multi wan inbound load balancing

No, unless inbound failover must work without DNS caches or a third-party front end. BGP requires your own ASN, at least a /24 of IPv4 or a /48 of IPv6 that upstreams will accept, and two ISPs willing to peer with you. For a home or small office, health-checked DNS or a proxy front end is far simpler.

Sources

  1. Firewall Settings (Advanced) - OPNsense documentation
  2. Rules - OPNsense documentation
  3. Network Address Translation - OPNsense documentation
  4. Multi WAN how-to - OPNsense documentation
  5. Dynamic DNS - OPNsense documentation
  6. Dynamic Routing - OPNsense documentation
  7. RFC 1794: DNS Support for Load Balancing
  8. RFC 8305: Happy Eyeballs Version 2: Better Connectivity Using Concurrency
  9. RFC 6724: Default Address Selection for Internet Protocol Version 6 (IPv6)
  10. RFC 7454: BGP Operations and Security
  11. RFC 5321: Simple Mail Transfer Protocol
  12. RFC 6598: IANA-Reserved IPv4 Prefix for Shared Address Space
  13. Multivalue answer routing - Amazon Route 53 Developer Guide
  14. Proxy modes - Cloudflare Load Balancing docs
  15. Standard steering options - Cloudflare Load Balancing docs
#opnsense #multi-wan#load-balancing#dns#bgp#port-forwarding

Related