What is the difference between a security group and a network ACL in AWS?
A security group is a stateful firewall attached to an instance's network interface, with allow rules only. A network ACL is a stateless firewall on a subnet, with numbered allow and deny rules checked in order. Because NACLs are stateless, you must allow return traffic explicitly, usually on ephemeral ports.
Explain it simply
Think of a hostel. The network ACL is the guard at the main gate of the hostel block. He checks a written list for everyone entering and everyone leaving, and he has no memory. Even if he let you in five minutes ago, he checks his list again when you walk out.
The security group is the lock on your own room door. It only has an "allowed" list. And it remembers: if you let a friend in, they can walk back out without being checked again.
So the NACL guards the whole subnet, is forgetful (stateless) and can say "no" to specific people. The security group guards one instance, remembers conversations (stateful) and only says "yes" to listed people.
Example
A web server in a public subnet:
Security group "web-sg" (attached to the web server)
Inbound: allow TCP 443 from 0.0.0.0/0
Inbound: allow TCP 22 from the office IP only
Outbound: allow all (default)
-> replies to HTTPS requests go out automatically (stateful)
Network ACL on the public subnet
Inbound rule 90: DENY all traffic from 203.0.113.0/24 (known bad range)
Inbound rule 100: ALLOW TCP 443 from 0.0.0.0/0
Inbound rule 110: ALLOW TCP 1024-65535 from 0.0.0.0/0 (replies to outbound calls)
Outbound rule 100: ALLOW TCP 1024-65535 to 0.0.0.0/0 (replies to clients)
Outbound rule 110: ALLOW TCP 443 to 0.0.0.0/0 (server calling APIs)
Rule *: DENY everything elseNotice the ephemeral port range (1024-65535). Clients connect from a random high port, so the NACL must let replies out to those ports. The security group did not need this.
How it works
| Security group | Network ACL | |
|---|---|---|
| Applies to | Network interface (instance level) | Subnet level |
| State | Stateful: return traffic allowed automatically | Stateless: return traffic needs its own rule |
| Rules | Allow only | Allow and deny |
| Order | All rules evaluated together | Lowest rule number first; first match wins |
| Default | New SG: no inbound, all outbound allowed | Default NACL allows all; a new custom NACL denies all |
| Can reference | Other security groups as a source | IP ranges (CIDR) only |
Traffic entering an instance passes the subnet's NACL first, then the instance's security group. Both must allow it.
A powerful security group feature is referencing another group. The database SG can say "allow port 5432 from app-sg", so any instance in the app group can connect, even when IPs change during auto scaling.
Real-world use
- Security groups do most of the daily work: web servers allow 443 from anywhere, app servers allow traffic only from the load balancer's SG, databases only from the app SG.
- NACLs are used as an extra, coarse layer: blocking a known bad IP range quickly during an attack, or enforcing that a database subnet never talks to the internet regardless of SG mistakes.
Common mistakes
- Trying to add a "deny" rule to a security group. You can't; use a NACL.
- Creating a custom NACL, allowing port 443 inbound and forgetting the outbound ephemeral ports, so the site times out.
- Opening SSH (22) or the database port to
0.0.0.0/0. - Using IP addresses in SG rules instead of referencing other security groups.
- Putting a broad ALLOW at a lower number than a DENY in a NACL; the allow matches first and the deny never runs.
What to say in the interview
"Security groups are stateful firewalls attached to instances, or more exactly to network interfaces. They only have allow rules, all rules are evaluated together, and return traffic is allowed automatically. Network ACLs work at the subnet level, are stateless, support both allow and deny, and are evaluated in rule-number order with the first match winning, so I must allow return traffic on ephemeral ports. In practice I rely on security groups, chaining them by referencing one group from another, and use NACLs as a coarse extra layer, for example to block an IP range."
Follow-up questions
- Why do NACLs need rules for ephemeral ports?
- How would you block a single malicious IP address?
- How do you let only the app servers connect to the database?
- If traffic is blocked, how do you find out whether the SG or the NACL dropped it?
Go deeper
Read these once the basics above feel easy.
VPC Flow Logs record accepted and rejected traffic per interface, which helps find which layer is blocking a connection. Also know connection tracking: because security groups are stateful, removing a rule may not immediately cut a connection that is already being tracked, while a NACL change applies to the very next packet. During an incident, a NACL deny is the faster way to stop traffic that is already flowing.
For application-level threats, neither SGs nor NACLs inspect content. Add AWS WAF on the load balancer or CloudFront for rules like SQL injection patterns and rate-based blocking, and AWS Network Firewall for stateful inspection across a VPC. Manage SG and NACL rules as code (Terraform or CloudFormation) so changes are reviewed.