Security Groups vs NACLs
Every major cloud gives you two firewall layers: instance-attached and subnet-attached. They behave differently — one is stateful, one is stateless; one only allows, the other can also explicitly deny; one is the right tool for almost everything, the other handles the edge cases. Understanding which is which keeps cloud network rules clean and prevents the common mistake of writing the same rule in both layers.
Side-by-side comparison
| Property | Security Group | Network ACL |
|---|---|---|
| Scope | Per-instance / ENI | Per-subnet |
| Stateful? | Yes — return traffic auto-allowed | No — must write both directions |
| Rule types | Allow only | Allow and deny |
| Rule evaluation | All rules evaluated; explicit allow wins | Numbered rules in order; first match wins |
| Default behavior | Default deny inbound; default allow outbound | Default allow both directions (until you change it) |
| Source/destination | IPs, CIDRs, other security groups, prefix lists | IPs, CIDRs only |
| Best for | Almost everything | Subnet-wide deny rules and defense in depth |
How stateful makes life easier
To allow a web server to receive HTTPS from anywhere on a stateful firewall: one rule. Allow inbound TCP 443 from 0.0.0.0/0. Return traffic on the established connection is automatically permitted. Nothing else needed.
To do the same on a stateless firewall: two rules.
Allow inbound TCP 443 from 0.0.0.0/0(the client to server)Allow outbound TCP from port 443 to ephemeral ports (1024-65535) on 0.0.0.0/0(the server response)
Multiplied across every port and protocol, the stateless rule set explodes. This is why security groups are the daily tool and NACLs are a specialized backstop.
Why NACLs still matter
Security groups have no concept of "deny." Every rule is an allow; absence of an allow means deny. NACLs can express "deny everything from this specific IP range" — a security group cannot. Use cases:
- Blocking a specific bad-actor IP or CIDR. Add a deny rule at the NACL; the IP is blocked regardless of any allow rules at security group level.
- Subnet-wide policy. "Nothing in the database subnet should ever talk to the internet." Easier to enforce at the NACL.
- Defense in depth. A misconfigured security group might allow something it shouldn't. A NACL provides a coarser backstop.
- Regulated environments. Some compliance frameworks require subnet-level controls as a separate layer.
Layered evaluation
When a packet enters a subnet:
- Subnet NACL inbound rules evaluate first. If denied, packet drops.
- Security group inbound rules on the destination instance evaluate next. If denied, packet drops.
- Instance / ENI receives the packet.
- Reply leaves the instance.
- Security group outbound rules evaluate. Default allow all. If denied, packet drops.
- NACL outbound rules evaluate. If denied, packet drops.
For a packet to reach an instance and a reply to return, all four checkpoints must allow it. The most restrictive layer wins.
Security group rule patterns
| Pattern | Example |
|---|---|
| Allow from a CIDR | Inbound TCP 443 from 0.0.0.0/0 |
| Allow from another SG | Inbound TCP 3306 from sg-database-clients |
| Allow within self | Inbound all traffic from this same SG (for cluster intra-node) |
| Allow from prefix list | Inbound TCP 443 from managed-prefix-list-cloudfront |
The "reference another SG" pattern is the most cloud-native and dynamic — it lets membership in groups define connectivity policy rather than IP enumeration.
NACL rule numbering
NACL rules are numbered (e.g., 100, 200, 300). The lowest number that matches a packet wins. Best practice:
- Leave gaps: use 100, 200, 300, not 1, 2, 3. New rules can be inserted between existing ones.
- Put specific deny rules below specific allow rules so allows match first when intended.
- End with explicit deny-all at high numbers (e.g., 32766) if the default doesn't already do this.
Common mistakes
- Same rules in SG and NACL. Duplicates maintenance burden with no security gain. Pick one as primary.
- NACL inbound but no NACL outbound (or vice versa). Connection works in one direction, replies dropped. Common with stateless rules.
- Forgetting ephemeral port range on stateless return traffic. The client uses a random high port; the NACL must allow return traffic to that range.
- Locking yourself out. Denying SSH at the NACL while you're connected via SSH. Bastion-host access broken until console intervention.
- 0.0.0.0/0 in production rules. Production should reference specific CIDRs or SGs. "Allow all from anywhere" is rarely the actual intent.
Auditing and review
Both security groups and NACLs are easy to accumulate cruft in. Regular review:
- Quarterly audit of all rules; remove any with no current justification.
- Tag rules with ticket numbers or owner.
- Use cloud-native tools (AWS Config, Azure Policy, GCP Security Command Center) to flag overly-permissive rules.
- Especially flag any rule with source 0.0.0.0/0 on non-public-facing services.
Frequently Asked Questions
What is a security group?
A stateful firewall that attaches to individual instances (or ENIs / network interfaces). Rules describe allowed inbound and outbound traffic. Because it is stateful, return traffic for an allowed outbound connection is automatically permitted back in without needing an explicit inbound rule.
What is a network ACL?
A stateless firewall that attaches to subnets. Rules are evaluated in order and can either allow or deny. Because it is stateless, return traffic must be explicitly allowed — you need separate inbound and outbound rules even for a simple TCP exchange. Used for coarse subnet-level controls and explicit deny rules.
What is the difference between stateful and stateless firewalls?
A stateful firewall tracks the state of connections — once a connection is allowed in one direction, the return packets are automatically allowed. A stateless firewall evaluates each packet independently with no memory of prior packets. Stateless is simpler and faster but requires double the rules; stateful is easier to write rules for but more complex internally.
Should I use security groups, NACLs, or both?
Use security groups for the primary policy — they are easier to manage and stateful. Use NACLs sparingly for cases that security groups cannot handle: explicit deny rules (security groups have no deny), subnet-wide blocks of bad IPs, or defense-in-depth at the subnet boundary. Most cloud architectures rely primarily on security groups with NACLs as a backup.
Can security groups reference other security groups?
Yes. Instead of listing IPs, a security group rule can reference another security group as the source or destination. This means "allow port 3306 from any instance with the app-server security group attached" rather than specific IPs. As instances are added and removed, the rule continues to work without updates.
Related Guides
More From This Section
All Cloud Networking Guides
VPCs, peering, NAT, transit gateways, egress costs, and load balancers.
Cloud DNS Architecture
How cloud DNS actually works — VPC resolvers, private hosted zones, conditional forwarding to on-prem, split-horizon…
Cloud Egress Costs Explained
Cloud egress pricing explained — AWS, Azure, GCP rates, inter-AZ vs inter-region, NAT processing, VPC endpoints,…
Run a Speed Test
Measure download, upload, ping, and jitter in your browser.