Azure Route Tables and NVAs: Why the Reply Took a Different Path
▶ Watch on YouTube & subscribe to The Stack Underflow
Before any rule allows or denies a packet, Azure has to decide where it goes next. The video follows five packets through Microsoft’s quickstart template userdefined-routes-appliance, where a route table sends traffic through a network virtual appliance (NVA), and shows what happens on the way back. This page walks the same five packets with the routes, the rules and the documentation behind every step.
The one-line version: routes decide the path, NSGs decide the verdict. The most specific route wins, and routes belong to the subnet a packet leaves from, so the reply can take a different way back.
Last verified against Microsoft Learn: 1 October 2026.
How Azure picks a route
Every subnet has a list of effective routes: an address prefix plus a next hop. Azure adds system routes for you:
| Prefix | Next hop | Meaning |
|---|---|---|
| The VNet’s own address space | Virtual network | Delivered inside the VNet |
| 0.0.0.0/0 | Internet | Everything else goes out |
| 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10 (and a few more) | None | Dropped |
A route table adds your own user-defined routes (UDRs). Each subnet can have zero or one route table, and its routes apply to traffic leaving that subnet.
When several routes contain the destination:
- Longest prefix wins. A
/24beats a/16, because it describes a smaller, more specific range. - Same prefix? Azure prefers a user-defined route, then a BGP route, then a system route. The overridden system route shows as Invalid in the effective routes.
What an NVA needs
A network virtual appliance is a VM that forwards traffic for others, often a firewall. A route with next hop Virtual appliance points at its private IP. Two things must be true for it to forward:
- IP forwarding must be enabled on the appliance’s Azure network card. It lets the NIC receive traffic not addressed to any of its own IPs, and send traffic with a different source IP. It’s off by default.
- The software inside the VM must forward too. Microsoft is explicit: IP forwarding is an Azure setting, and the VM must also run an application that forwards the traffic.
The lab
One VNet, 10.1.0.0/16, three subnets, one VM each:
| VM | Role | Subnet | Address |
|---|---|---|---|
| vm0 | Front end | Frontend | 10.1.0.4 |
| vm1 | Appliance (NVA), IP forwarding on | NVA | 10.1.1.4 |
| vm2 | Back end | Backend | 10.1.2.4 |
- Route table “Basic NVA”, attached only to the Frontend subnet, has one route:
10.1.2.0/24→ virtual appliance10.1.1.4. - Default NSG, shared by all three subnets, has one inbound rule: 100 rdp_rule allows TCP 3389 from the Internet. No outbound rules, so the default outbound rules apply. No VM’s network card has its own NSG.
vm0 and vm2 get dynamic private addresses; the video shows them as .4, the usual first address. Azure normally assigns the next available address, but doesn’t guarantee it.
Five packets, step by step
Packet 1: the front end connects to the back end. vm0 10.1.0.4 → vm2 10.1.2.4, TCP 3389.
Four routes contain 10.1.2.4: your 10.1.2.0/24, the VNet’s 10.1.0.0/16, 0.0.0.0/0, and the reserved 10.0.0.0/8. The /24 is the most specific, so the packet goes to vm1, the appliance. Default NSG checks it leaving Frontend (AllowVnetOutBound 65000) and entering the NVA subnet: rule 100 is for Internet traffic, so no match, and AllowVNetInBound 65000 allows it. vm1 has IP forwarding on, so Azure hands it a packet addressed to someone else, and it sends it on. The NVA subnet has no route table, so the system route for 10.1.0.0/16 delivers it to the Backend subnet, where 65000 allows it again. Allowed, last allowed by Default NSG 65000 AllowVNetInBound. The route table, not the NSG, decided the path.
The way back: the reply leaves the Backend subnet, which has no route table, so it goes straight to vm0, not through the appliance. The NSGs don’t need a rule for it: they’re stateful.
(The video’s model checks the NVA subnet’s NSG against the packet’s original source and destination. That follows from NSGs matching on the five-tuple; Microsoft doesn’t state this case explicitly.)
Packet 2: the back end connects to the front end. vm2 10.1.2.4 → vm0 10.1.0.4, TCP 3389.
The Backend subnet has no route table, so 10.1.0.0/16 sends it straight to vm0. Default NSG allows it out (65000) and in (65000). Allowed, and it never touches the appliance.
The way back: routing decides the reply separately. The reply leaves the Frontend subnet, whose route sends 10.1.2.0/24 to the appliance, so the reply goes through vm1, which the request never passed.
Packet 3: what if IP forwarding is off on the appliance? vm0 → vm2, TCP 3389, with IP forwarding turned off on vm1’s NIC. The route still points at 10.1.1.4, and the NSGs still allow it, as in packet 1. But without IP forwarding, Azure won’t hand vm1’s NIC a packet addressed to 10.1.2.4. Dropped at vm1. The route alone isn’t enough: the NIC setting and the forwarding software must both be in place.
Packet 4: Remote Desktop from the Internet to the front end. 203.0.113.10 → vm0’s public IP, TCP 3389. Azure translates the public address to the private one, 10.1.0.4, before any NSG sees it. Default NSG inbound: rule 100 rdp_rule matches. Allowed. The reply comes back the same way.
Packet 5: a web request from the Internet to the back end. 203.0.113.10 → vm2’s public IP, TCP 80. After translation to 10.1.2.4, Default NSG checks inbound: rule 100 is for 3389, not 80. Nothing else matches, so DenyAllInbound (65500) blocks it. Denied by Default NSG 65500.
| # | Packet | Path | Verdict | Decided by |
|---|---|---|---|---|
| 1 | vm0 → vm2, TCP 3389 | via vm1 (UDR /24); reply direct | Allowed | Default NSG 65000 |
| 2 | vm2 → vm0, TCP 3389 | direct; reply via vm1 | Allowed | Default NSG 65000 |
| 3 | vm0 → vm2, IP forwarding off | to vm1, stops | Dropped | IP forwarding off |
| 4 | Internet → vm0, TCP 3389 | public IP → 10.1.0.4 | Allowed | Default NSG 100 rdp_rule |
| 5 | Internet → vm2, TCP 80 | public IP → 10.1.2.4 | Denied | Default NSG 65500 DenyAllInbound |
Why asymmetric paths matter
In packets 1 and 2, one connection took two different paths. That’s fine for plain forwarding. But Microsoft notes that most stateful NVAs, such as firewalls, require traffic symmetry: an appliance that sees only one direction of a connection usually drops it. The usual fixes are route tables on both sides, so both directions pass the appliance, or SNAT on the appliance so replies come back to it.
The template today
The routing in the video works exactly as shown, but the template as published uses Basic public IP addresses, and Basic SKU public IPs were retired on 30 September 2025. Standard public IPs are static and closed to inbound traffic until an NSG allows it, which rule 100 does for RDP here. To deploy the lab yourself, use Standard static public IPs and a current VM size and image.
Pause & Prove: the questions from the video
1. The pinned question
Only the front-end subnet has the route “10.1.2.0/24 → appliance 10.1.1.4”. The back end opens a connection to the front end. Does the request go through the appliance? Does the reply?
The request goes straight; the reply goes through the appliance. The request leaves the Backend subnet, which has no route table, so the VNet system route applies. The reply leaves the Frontend subnet, whose UDR sends 10.1.2.0/24 to the appliance. Routes belong to the subnet a packet leaves from.
2. The Community poll
Two routes match a packet’s destination: 10.1.0.0/16 (the VNet) and your UDR 10.1.2.0/24 → appliance. Which one does Azure use?
- The VNet route: system routes win. No: user-versus-system only breaks a tie between routes with the same prefix; here the prefixes differ, so the longest prefix decides.
- The /24: the most specific route wins. ✓ Longest prefix match decides first.
- Whichever was created first. Creation order plays no part in route selection.
- Both: the packet is copied. Azure picks one route per packet.
For the same prefix, your UDR would beat the system route.
Related Shorts
- Two Azure routes match. Which one wins? (packet 1, the route lookup)
- The request went straight, the reply went through the appliance (packet 2)
- Your Azure route points at an appliance, and the packet still dies (packet 3)
Before / after this video
- Before: How Azure NSGs Allow or Deny a Packet (video 1: priorities, first match, default rules)
- After: Azure Subnet NSG vs NIC NSG (video 3), then How Azure Firewall Decides (video 4), where a 0.0.0.0/0 UDR sends every spoke’s traffic to a firewall
Sources
Read on 1 October 2026:
- Azure virtual network traffic routing: system routes and None ranges, zero or one route table per subnet, longest prefix match, user > BGP > system, Invalid state, virtual appliance next hop and IP forwarding
- Create, change, or delete a network interface: what IP forwarding does, off by default, the VM must also run forwarding software
- Azure network security groups overview: default rules, statefulness, NAT before inbound NSG processing, five-tuple matching
- Public IP addresses in Azure: Basic retirement on 30 September 2025, Standard is static and secure by default
- Private IP addresses: dynamic allocation is usually, not always, the next address
- Deploy highly available NVAs: stateful NVAs need traffic symmetry; SNAT or routing keeps it
- Network template: “userdefined-routes-appliance” from Azure/azure-quickstart-templates (MIT License)
Change notes
- 1 Oct 2026: first published.
Not affiliated with or endorsed by Microsoft. Found a mistake? Tell us in the video’s comments and we’ll correct this page.
Found this useful? The deep version lives on YouTube — new breakdowns of how AI dev tools actually work, weekly.
Subscribe on YouTube →Prefer email? Get the free newsletter: one failure, traced step by step, about once a week.