Your Spoke VM Can't Reach the Internet Through the Firewall. Here's the Diagnostic Order That Actually Works.
The ticket looks simple. A VM in a spoke network can't reach the internet, even though it's supposed to route through the hub firewall. Someone checks the NSG. Looks fine. Someone checks the route table. Also looks fine. An hour later, still broken, and now there are three people staring at the same two screens that already looked correct the first time.
The problem isn't usually that nobody knows Azure networking. It's that there's no fixed order to check things in, so everyone jumps to their favorite suspect first. Here's the order that actually finds the break, and the one step almost everyone skips.
Start with the route, not the NSG
Before touching anything, run this:
bash
az network nic show-effective-route-table \
--resource-group
--name
--output table
Look at the 0.0.0.0/0 route. The next hop should say VirtualAppliance, pointing at the firewall's private IP. If it says Internet instead, the spoke subnet either has no user-defined route to the firewall, or it has one that was never actually associated with the subnet. Those are two different problems with the same symptom, and this command tells you which one you're dealing with before you touch anything else.
This is the step people skip in favor of the NSG, because NSGs feel like the more familiar suspect. But a wrong route means traffic never gets a chance to hit an NSG rule in the first place, so checking NSGs first is checking the wrong layer.
Confirm with next-hop, not just the route table
The effective route table tells you what should happen. This confirms what actually would happen for a specific destination:
bash
az network watcher show-next-hop \
--resource-group
--vm
--source-ip
--dest-ip 8.8.8.8
Expect nextHopType to say VirtualAppliance. If it says Internet, the route is wrong, consistent with what you just found. If it says None, that's a different and more specific problem: Azure is actively dropping the traffic, not misrouting it. That distinction matters for what you fix next.
Now check the NSG, with the tool that names the actual rule
bash
az network watcher test-ip-flow \
--resource-group
--vm
--direction Outbound \
--protocol TCP \
--local
--remote 8.8.8.8:443
This doesn't just say allowed or denied. It names the exact NSG rule responsible. NSGs apply at both the subnet and NIC level, and traffic has to clear both, so don't stop at the first NSG you find and assume you've checked everything.
If the route is missing, here's the fix:
bash
az network route-table create -g
az network route-table route create -g
-n default-to-fw --address-prefix 0.0.0.0/0 \
--next-hop-type VirtualAppliance --next-hop-ip-address
az network vnet subnet update -g
-n
Three commands, and the third one is the one people forget. A route table that exists but was never associated with the subnet does nothing. It'll sit there looking correct in the portal while traffic keeps ignoring it entirely.
The one that actually causes the "I fixed it and it's still broken" ticket
Here's the case that burns the most time. Every check above passes. Route is right, NSG is right, and the VM still can't reach the internet. This is almost always asymmetric routing.
Azure Firewall is stateful. It remembers the connections it allows outbound. If your spoke VM's traffic goes out through the firewall correctly, but the return traffic takes a different path back, one that bypasses the firewall, the firewall never sees the reply it's expecting and drops the whole exchange. From the VM's side, it just looks like the request vanished.
This usually happens when the hub or firewall subnet's own route table sends return traffic around the firewall instead of back through it. Check that the firewall subnet's routing doesn't create a shortcut, symmetry has to hold in both directions, not just the outbound leg.
Two more things worth knowing before you close the ticket
Azure Firewall denies by default, even with perfect routing. Confirm a network or application rule actually allows the spoke's source range to the destination. And if the firewall has no explicit management NIC, the AzureFirewallSubnet itself needs its own 0.0.0.0/0 route with a next hop of Internet, or the firewall has no way to egress at all.
One more, easy to overlook: traffic between two subnets in the same VNet does not route through the firewall by default, even if you've set a 0.0.0.0/0 UDR on both. Azure's system route for same-VNet traffic is more specific and wins. Forcing intra-VNet traffic through inspection needs its own per-subnet route tables, that's a deliberate design decision, not something a single UDR handles automatically.
The pattern worth keeping
Route first, then next-hop, then NSG, then look for asymmetry if everything else checks out. In that order, because each step rules out an entire category before you spend time on the next one. Checking NSGs before routes, or assuming symmetric traffic without checking, is how a ten-minute diagnosis turns into an hour of re-checking things that were already fine.
If you found this useful, hit the Subscribe button below to get more articles like this delivered straight to your inbox.
Want to see what our AI agents do with a ticket like this? Start a free trial and throw your trickiest network issue at them.
What's the networking bug that's cost you the most time chasing the wrong layer? Tell me in the comments.
Discussion
Share it in the comments: we're happy to walk through the specifics.
No comments yet. Be the first to share your thoughts.
Leave a Comment