About this article: AI-assisted writing, reviewed against the cited sources. This is source research, not a lab validation of the procedures. Check the stated versions and your own recovery requirements before making changes.
An asymmetric path in Azure usually starts with two route lookups giving different answers, though the routes can also agree and the two directions still land on different appliance instances. Azure routes each packet from the subnet it leaves, so the forward path and the return path are decided by different route tables, and nothing makes them agree. If a stateful appliance sits on only one of those paths, it never sees the whole conversation and drops the part it does see.
The fix starts by proving each direction separately: run the next hop lookup from each end, then capture on the appliance to see which half of the flow arrives.
The commands come from Microsoft's published reference and were not run in a lab for this article. Addresses are examples.
Why it matters
These faults are confusing because each check looks healthy on its own. The network security group (NSG) allows the port. The appliance's rule allows the port. Peering is connected. Yet the connection hangs after the first packet, or works from one direction only.
Microsoft describes the mechanism for Azure Firewall behind a public load balancer: the firewall is stateful, so it discards a return packet that belongs to no session it knows about. Most third-party firewalls behave the same way, and Microsoft notes that most stateful appliances require traffic symmetry.
This article covers the Azure-specific route mechanics. The published article Connections Half-Work Through Two Gateways: Proving Asymmetric Routing deals with the wider concept.
How Azure picks the route
Three rules explain nearly every case.
Longest prefix first. Azure selects by longest prefix match on the destination address. A /24 beats a /16, whatever the source of either route.
Then route type. For an identical prefix, the priority is user-defined route, then BGP route, then system route.
One exception to remember. System routes for traffic within the virtual network, across peerings and to service endpoints are preferred even if BGP routes are more specific. A BGP advertisement cannot steal that traffic. A user-defined route can override the virtual network and peering routes, but a route with a service endpoint as its next hop type cannot be overridden, even with a route table.
A route table is attached to a subnet, and it applies to outbound traffic leaving that subnet. That one fact is the source of most asymmetry. Steering traffic out of subnet A towards the appliance says nothing about how the reply leaves subnet B.
Where the asymmetry comes from
The cases below follow from those rules. The worked reasoning is editorial and uses example ranges: hub with an appliance at 10.0.1.4, spoke at 10.1.0.0/16, on-premises at 192.168.0.0/16.
The gateway subnet has no route to the appliance
This is the classic hub design fault.
The spoke subnet has a user-defined route (UDR) of 0.0.0.0/0 to 10.0.1.4. So replies from the spoke to on-premises go to the appliance.
The gateway, however, reaches the spoke with an ordinary peering system route. Traffic arriving from on-premises goes directly from the gateway to the spoke workload and never touches the appliance. The appliance sees only the reply, has no session for it, and drops it.
The repair is a route table on the GatewaySubnet sending the spoke ranges to the appliance. Be precise with its contents:
- Do not add a 0.0.0.0/0 route there. Microsoft says this can prevent a VPN gateway from functioning properly.
- Do not disable route propagation on
GatewaySubnet. If it is disabled, the gateway stops functioning.
A BGP route beats your default route
A UDR of 0.0.0.0/0 is the shortest prefix there is. If on-premises advertises 192.168.0.0/16 over BGP and the spoke learns it through gateway transit, that /16 wins on length. Spoke traffic to on-premises goes directly to the gateway and bypasses the appliance.
Two fixes exist. You can add a matching or more specific UDR for the on-premises ranges, since a UDR wins at equal length. Or you can disable gateway route propagation on the spoke's route table, which removes learned routes so that the default route applies. Propagation applies to subnets, so check every spoke subnet.
Traffic between subnets of one virtual network
A subnet's own VNet system route carries traffic to neighbouring subnets directly. If only one of the two subnets has a UDR towards the appliance, the flow is asymmetric from the start.
Routing a whole VNet range through a firewall has its own trap. Microsoft warns that using the VNet address range as the UDR target also sends traffic between machines in the same subnet through the firewall, and an appliance deployed in the same subnet as the workloads can create a routing loop. Keep the appliance in its own subnet.
A public load balancer in front of a default route
Inbound traffic reaches a workload through a public load balancer, and the subnet's 0.0.0.0/0 UDR sends the reply to the firewall's private address. The firewall sees an unsolicited packet.
Microsoft's documented repair for Azure Firewall is to bring internet traffic in through the firewall's public address, where a NAT rule translates it to the load balancer's public address. You then add a specific route for the firewall's public address with next hop Internet, so the return bypasses the default route. Where a NAT gateway sits on the firewall subnet, the route must use the NAT gateway's public address instead.
Microsoft's stated preference is simpler: use an internal load balancer with the firewall.
Several appliance instances
Now the path is right but the instance is wrong. Two appliances, each with its own session table, can each see half of a flow. The insertion method decides whether that can happen.
| Insertion method | Symmetry, per Microsoft's guidance |
|---|---|
| One internal Standard load balancer, one backend pool, single-NIC appliances | Guaranteed, as both directions traverse the same load balancer |
| Dual-NIC appliances, an internal load balancer per direction | Requires SNAT |
| Public and internal load balancers | Requires SNAT |
| Azure Route Server | Equal-cost multipath picks an instance per flow, so SNAT is required for symmetry |
| Gateway Load Balancer | Guaranteed without SNAT, for flows to and from the internet; no east-west support |
| Dynamic public IP and UDRs | Guaranteed, active/passive only, one to two minutes to converge |
These come from Microsoft's appliance high-availability guide. The internal load balancer works this way because the load-balancing decision is made per flow, on the five-tuple, with a high availability ports rule covering all ports. Symmetry is not guaranteed with two or more load balancers, backend pools or frontend IP configurations.
The Azure Route Server row applies to stateful appliances. Microsoft notes that some appliances that use it are usually stateless, so symmetry is not a problem for them.
Source NAT (SNAT) forces symmetry by making replies come back to the instance that handled the request. It also hides the original client address from the workload, which may matter for logging. Azure Firewall itself does not SNAT to private RFC 1918 and RFC 6598 destinations by default, though application rules always SNAT.
Dual-NIC probe replies
A separate symmetry problem affects the health probe. Azure's probes always come from 168.63.129.16, and a dual-NIC appliance must reply through the interface that received them. Microsoft notes this typically needs multiple routing tables in the operating system, since destination-based routing would otherwise send replies through a single NIC.
When the replies leave the wrong interface, the backend looks unhealthy for reasons unrelated to the traffic you are debugging.
Basics that imitate asymmetry
Before chasing routes, confirm that the appliance can forward at all.
IP forwarding must be enabled on every network interface of the VM that forwards traffic. The setting disables Azure's source and destination check on that interface, and its default is disabled. You can read it on the interface's IP configurations page.
The Azure setting is not enough on its own. The VM must also run software able to forward, and you may need to enable forwarding in the operating system too.
NSG evaluation order matters on the appliance's own subnet. Inbound, Azure processes the subnet NSG and then the interface NSG. Outbound, it processes the interface and then the subnet. Traffic needs to be allowed by both.
NSGs apply to traffic passing through an appliance's interface like any other. A rule built only around the workload's address can miss what the appliance actually receives once NAT is involved.
Also remember what rule changes do. Microsoft states that NSG rules only affect new connections. An existing session continues after a rule is removed, so test with a fresh connection.
Proving it, one direction at a time
Step 1: next hop from both ends
az network watcher show-next-hop -g <rg> --vm <client-vm> \
--source-ip 10.1.0.10 --dest-ip 10.2.0.10
az network watcher show-next-hop -g <rg> --vm <server-vm> \
--source-ip 10.2.0.10 --dest-ip 10.1.0.10
The result gives the next hop type, its IP address and the route table ID. System-sourced routes show System Route as the route table.
Both directions should name the appliance. If one shows VirtualAppliance and the other VirtualNetworkPeering, you have your answer. A next hop type of None means the traffic is being dropped by routing. That differs from a next hop IP of None beside a gateway or appliance type, which Microsoft says might mean the device is not running or not fully configured.
For flows that enter through a gateway, this command cannot start from the gateway. List the routes applied to GatewaySubnet from its route table, and reason about what a packet leaving that subnet will do.
Step 2: effective routes and rules
az network nic show-effective-route-table -g <rg> -n <nic> --output table
az network nic list-effective-nsg -g <rg> -n <nic>
Effective routes show the combined result of default, BGP and user-defined routes for that interface's subnet. The portal lists them only if the NIC is attached to a running VM, so a stopped appliance gives you nothing here. Look at the most specific match for your destination, not only the default.
Step 3: separate filtering from routing
az network watcher test-ip-flow -g <rg> --vm <vm> --direction Outbound \
--protocol TCP --local 10.1.0.10:* --remote 10.2.0.10:443
IP flow verify reports the allowing or denying rule. It tests TCP and UDP only, and only security and admin rules on that interface.
It can rule out an NSG, but it cannot detect asymmetry. A clean result here while traffic still fails points back to routes or to the appliance.
Connection troubleshoot checks further, including missing routes, NSG blocks and guest firewall rules, and runs from a source resource. Treat its output as one view of the forward path.
Step 4: read the flow logs
Virtual network flow logs record the interface, five-tuple, direction, flow state and throughput, collected at one-minute intervals. Flow state is B for begin, C continuing, E end and D deny.
What to look for:
- Records for the appliance interface carrying only one direction of a client-server pair.
- A workload interface showing outbound replies, where the appliance interface has no matching inbound request.
Those are editorial reading aids, not Microsoft diagnostics. Three cautions apply.
NSG flow logs retire on 30 September 2027 and can no longer be newly created, so use virtual network flow logs. Microsoft recommends disabling the NSG versions first on the same workloads, to avoid duplicates and extra cost.
VM-to-ExpressRoute outbound flows are not recorded if logging is enabled only on the ExpressRoute gateway subnet. Record them at the VM's subnet or interface. The same applies when traffic bypasses the gateway under ExpressRoute FastPath.
Traffic may also appear under a platform rule rather than yours in some cases, including return traffic needing no rule evaluation. Do not read that as a misconfiguration.
Step 5: capture on the appliance
The conclusive test is packet capture on every appliance interface in the path. On a Linux-based appliance, a standard tool works, and vendors provide equivalents:
tcpdump -ni eth0 host 10.1.0.10 and host 10.2.0.10 and tcp port 443
| Seen on the appliance | Meaning |
|---|---|
| SYN arrives, SYN-ACK never does | Reply takes another path, or the server never received the SYN |
| Only SYN-ACK arrives | Forward path bypasses the appliance |
| SYN and SYN-ACK on different instances | Instance asymmetry, so check the load balancer or add SNAT |
| Both directions present, but the session is dropped | Investigate appliance session policy or NAT, then verify downstream delivery; these captures alone do not clear every routing layer |
That table is editorial. Capture on the workload at the same time, so you can tell a packet that never left from one that never arrived.
What to choose and why
These are editorial recommendations.
| Situation | Choice |
|---|---|
| Hub and spoke, on-premises inspected | Spoke UDRs and a matching GatewaySubnet route table for spoke ranges |
| Spoke learning on-premises by BGP | Disable propagation, or add specific UDRs |
| Two or more stateful instances, east-west traffic | One internal load balancer with HA ports, single NIC if the vendor supports it |
| Inbound internet with a public load balancer | Bring traffic in through the firewall's public address and add the return route |
| Client IP must reach the workload, internet-facing flows | Prefer designs that do not need SNAT, such as Gateway Load Balancer, which does not cover east-west flows |
Keep route tables small and named after their function. A route table per subnet role makes a missing entry far easier to spot in review.
Risks and limits
Warning: applying a route table changes the path of every flow leaving that subnet. Change
GatewaySubnetroutes only in an approved window, with console access to the appliance and a recorded rollback. A wrong entry can cut remote access to the very systems you need to fix it.
- Failover is not instant. For the load balancer pattern, Microsoft gives probes every five seconds, three failures, and usually 10 to 15 seconds to converge. Assume that sessions held on a failed instance are lost, which is an editorial caution.
- Effective routes and rules are not displayed in the portal for a private endpoint's interface, so do not count on that view when a private endpoint is one end of the flow.
- Virtual network flow logs are charged per gigabyte above a free tier, with storage billed separately. Scope them to what you are investigating.
- Check the appliance vendor's documentation for high availability behaviour. Microsoft says to confirm supported scenarios with the provider.
Sources
Opened 9 November 2026. Sources behind the corrections reopened 10 November 2026.
- Virtual network traffic routing
- Deploy highly available network virtual appliances
- Integrate Azure Firewall with a load balancer
- Azure Firewall FAQ
- High availability ports overview
- How network security groups filter traffic
- Network security groups overview
- Create and change a network interface
- Next hop overview
- IP flow verify overview
- Connection troubleshoot overview
- Virtual network flow logs
- Read flow logs
- az network watcher
- az network nic
- What is a private endpoint?
This article was originally published on .