Azure Subnet NSG vs NIC NSG: Why the Packet Still Got Denied

September 30, 2026 · Azure Networking, Packet by Packet: NSGs, Route Tables, ASGs and Azure Firewall (part 3)

▶ Watch on YouTube & subscribe to The Stack Underflow

An NSG can be attached to a subnet, to a server’s network card (NIC), or to both. When a packet meets two guards, which one wins? The video builds a small shop network (two web servers, a database and an admin jump box) and sends six packets through it until the answer is obvious. This page walks the same packets with every rule and the Microsoft documentation behind them.

The one-line version: inbound, the subnet’s NSG goes first, then the NIC’s; outbound, the reverse. Every NSG on the path must allow the packet, and each one ends with its own default rules.

Last verified against Microsoft Learn: 1 October 2026.

The order of the guards

Microsoft documents a fixed order:

DirectionFirstSecond
InboundNSG on the subnetNSG on the network interface
OutboundNSG on the network interfaceNSG on the subnet

The same order applies to traffic between VMs in the same subnet. If the first NSG denies, the second never evaluates the packet. To let traffic in, both must allow it.

Each NSG is complete on its own: it has its own custom rules and its own default rules (65000 AllowVNetInBound, 65001 AllowAzureLoadBalancerInBound, 65500 DenyAllInbound, and three outbound). That detail decides packet 2.

A design note. Microsoft’s guidance today is to avoid associating NSGs with both a subnet and its network interfaces at the same time, because overlapping rules cause filtering problems that are hard to troubleshoot. The video layers them on purpose, to show the order.

Application security groups

An application security group (ASG) is a named group of NICs. You put each server’s NIC into a group (like web-servers or db-servers) and write rules that name the group instead of IP addresses. The rule then follows the servers, not their addresses.

  • A rule that names an ASG applies only to NICs that are members of it. A NIC that isn’t a member isn’t affected, even if the NSG is on its subnet.
  • All NICs in one ASG must be in the same virtual network, and when a rule uses ASGs as both source and destination, both groups must be in the same VNet.
  • A rule can’t name multiple service tags or ASGs in one field.
  • A NIC can belong to more than one ASG.

The shop network

This network was written for the video (it isn’t a Microsoft template). One VNet, 10.20.0.0/16:

ServerSubnetAddressASGNIC NSG
web-1web 10.20.1.0/2410.20.1.4web-serversweb-1-nic-nsg
web-2web 10.20.1.0/2410.20.1.5web-serversnone
db-1db 10.20.2.0/2410.20.2.4db-serversnone
jump-1admin 10.20.9.0/2410.20.9.4nonenone

web-1 and web-2 have Standard public IPs. The admin subnet has no NSG.

NSGRuleMatchesAccess
web subnet NSG100 Allow-HTTPS-InternetTCP 443 from InternetAllow
web subnet NSG110 Allow-SSH-AdminTCP 22 from 10.20.9.0/24Allow
db subnet NSG100 Allow-SQL-From-WebTCP 1433, ASG web-servers → ASG db-serversAllow
db subnet NSG4000 Deny-VNet-Inboundanything, VirtualNetwork → VirtualNetworkDeny
web-1 NIC NSG100 Deny-SSHTCP 22 from anywhereDeny

All of these are inbound rules. None of the NSGs has custom outbound rules, so their default outbound rules apply.

Six packets, step by step

Packet 1: a customer opens the shop on web-2. 203.0.113.25 → web-2, TCP 443. Azure translates web-2’s public IP to 10.20.1.5 before any NSG sees it. web subnet NSG: rule 100 matches. web-2’s NIC has no NSG. Allowed by web subnet NSG rule 100.

Packet 2: the same customer, on web-1. 203.0.113.25 → web-1 (10.20.1.4), TCP 443. web subnet NSG: rule 100 allows it, as before. Then web-1’s NIC NSG checks it: rule 100 Deny-SSH is for port 22, not 443, so no match. No other custom rule exists, so that NSG’s own default rules decide. The source is the Internet, not the VNet, so 65000 doesn’t match, and DenyAllInbound (65500) blocks it. Denied by web-1 NIC NSG rule 65500. The subnet said yes; the NIC said no.

Packet 3: the admin connects to web-2 with SSH. jump-1 10.20.9.4 → web-2 10.20.1.5, TCP 22. The system route for 10.20.0.0/16 is the most specific, so the packet stays in the VNet. jump-1’s NIC and the admin subnet have no NSG. web subnet NSG: rule 100 is for 443, no match; rule 110 Allow-SSH-Admin matches. web-2’s NIC has no NSG. Allowed by web subnet NSG rule 110.

Packet 4: the admin connects to web-1 with SSH. jump-1 → web-1 10.20.1.4, TCP 22. web subnet NSG allows it with rule 110, just as before. Then web-1’s NIC NSG: rule 100 Deny-SSH matches. Denied by web-1 NIC NSG rule 100. Inbound traffic meets the subnet NSG first and the NIC’s second; the subnet allowed SSH from the admin subnet, but web-1’s own NSG denies it.

Packet 5: web-2 queries the database. web-2 10.20.1.5 → db-1 10.20.2.4, TCP 1433. Leaving: web-2’s NIC has no NSG; web subnet NSG has no outbound rules, so AllowVnetOutBound (65000) allows it. Arriving: db subnet NSG rule 100 Allow-SQL-From-Web matches, because web-2 is in web-servers and db-1 is in db-servers. Allowed by db subnet NSG rule 100. The rule names groups, not addresses: add a new web server’s NIC to web-servers and it’s allowed too, without touching the rule.

Packet 6: the jump box tries the database directly. jump-1 10.20.9.4 → db-1, TCP 1433. No NSG on jump-1’s NIC or the admin subnet. db subnet NSG: rule 100 needs a source in web-servers, and jump-1 isn’t a member, so no match. Rule 4000 Deny-VNet-Inbound matches. Denied by db subnet NSG rule 4000. Without rule 4000, the default AllowVNetInBound (65000) would have let the jump box in.

#PacketVerdictDecided by
1Internet → web-2, TCP 443Allowedweb subnet NSG 100
2Internet → web-1, TCP 443Deniedweb-1 NIC NSG 65500 DenyAllInbound
3jump-1 → web-2, TCP 22Allowedweb subnet NSG 110
4jump-1 → web-1, TCP 22Deniedweb-1 NIC NSG 100 Deny-SSH
5web-2 → db-1, TCP 1433Alloweddb subnet NSG 100 (ASGs)
6jump-1 → db-1, TCP 1433Denieddb subnet NSG 4000

What to remember

  • Inbound: subnet NSG, then NIC NSG. Outbound: the reverse.
  • Every NSG on the path must allow the packet, and each has its own default rules at the bottom.
  • ASGs let a rule follow servers instead of addresses.
  • To stop traffic inside the VNet, write your own deny with a number below 65000.

Pause & Prove: the questions from the video

1. The pinned question

The web subnet’s NSG allows HTTPS from the Internet at priority 100. web-1’s NIC has its own NSG with a single rule: deny SSH (port 22). A customer opens HTTPS to web-1. Allowed or denied, and by which rule?

Denied, by web-1’s NIC NSG, rule 65500 DenyAllInbound. The subnet NSG allows it, but inbound traffic must also pass the NIC’s NSG. That NSG has no rule allowing 443, so its own default rules decide.

2. The Community poll

A packet comes in to a VM whose subnet and network card both have an NSG. Which NSG checks it first?

  • The network card’s NSG. That’s the order for outbound traffic.
  • The subnet’s NSG. ✓ Inbound: subnet first, then the NIC.
  • Whichever has the lower rule number. Priority numbers order rules inside one NSG, not NSGs against each other.
  • Both at the same time, and allow wins. They’re evaluated in sequence, and both must allow.

Before / after this video

Sources

Read on 1 October 2026:

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.