Security Group vs NACL in AWS: Differences with Examples
A security group protects an instance (its network interface), is stateful (replies are allowed automatically), has allow rules only, and evaluates all its rules. A network ACL protects a whole subnet, is stateless (return traffic needs its own rule), has allow and deny rules, and evaluates them in rule-number order, first match wins. Use security groups for everyday access, and a NACL when you must deny an IP or add a subnet-wide guard.
Come on, friends! A society has two kinds of security: the security guard at the main gate – he is for the whole society, checks both people coming in and going out, according to his list. And each flat's door lock – it is only for that flat, and a guest who came in is not checked again when leaving. The guard is the NACL, the lock is the security group. Got it? Now let's see the real technical difference.
चला मित्रांनो! Society मध्ये दोन सुरक्षा असतात: main gate चा security guard – तो सगळ्या society साठी, येणाऱ्या आणि जाणाऱ्या दोघांना check करतो, यादीप्रमाणे. आणि प्रत्येक flat चं door lock – ते फक्त त्या flat साठी, आणि एकदा आत आलेला पाहुणा बाहेर जाताना पुन्हा check होत नाही. Guard म्हणजे NACL, lock म्हणजे security group. समजलं का? आता खरा technical फरक बघूया.
चलो दोस्तों! Society में दो तरह की सुरक्षा होती है: main gate का security guard – वह पूरी society के लिए है, आने वाले और जाने वाले दोनों को check करता है, list के हिसाब से. और हर flat का door lock – वह सिर्फ उस flat के लिए है, और एक बार अंदर आया मेहमान बाहर जाते समय फिर से check नहीं होता. Guard यानी NACL, lock यानी security group. समझ आया? अब असली technical फर्क देखते हैं.
Quick answer
| Security group | Network ACL | |
|---|---|---|
| Works at | Instance level (ENI) | Subnet level |
| State | Stateful: reply traffic allowed automatically | Stateless: reply traffic must be allowed by a rule |
| Rules | Allow only | Allow and Deny |
| Evaluation | All rules are evaluated together | In order, lowest rule number first; first match wins |
| Default (custom one) | New SG: no inbound, all outbound allowed | New custom NACL: denies all in and out |
| Default (VPC's default) | Default SG: inbound from itself, all outbound | Default NACL: allows all in and out |
| Source can be | CIDR, prefix list, or another security group | CIDR only |
| Applies to | Only instances you attach it to | Every instance in the associated subnets |
See both for a subnet and an instance with the CLI:
aws ec2 describe-network-acls --filters Name=association.subnet-id,Values=subnet-0123456789abcdef0 --query "NetworkAcls[].Entries[]" --output table
aws ec2 describe-security-groups --group-ids sg-0123456789abcdef0 --query "SecurityGroups[].IpPermissions[]" --output table
What do I need to understand first?
- A VPC with a subnet and an EC2 instance in it. If you are new, start with Open port 80 and 443 in a security group.
- Traffic from outside to your instance passes two checks: first the network ACL at the subnet edge, then the security group at the instance. Both must allow it.
How does one request pass the NACL and the security group?
A request meets the network ACL at the subnet edge, then the security group at the instance. The reply leaves the security group automatically (stateful), but the network ACL checks it again (stateless), so outbound ports 1024–65535 must be allowed.
Request आधी subnet च्या edge वर network ACL ला भेटते, मग instance वर security group ला. Reply security group मधून आपोआप बाहेर जातो (stateful), पण network ACL तो पुन्हा check करतो (stateless), म्हणून outbound ports 1024–65535 allow करावे लागतात.
Request पहले subnet के edge पर network ACL से मिलती है, फिर instance पर security group से. Reply security group से अपने आप बाहर जाता है (stateful), पर network ACL उसे फिर से check करता है (stateless), इसलिए outbound ports 1024–65535 allow करने पड़ते हैं.
- A browser sends a request to port 80. The NACL inbound rules are checked in order: rule 100 allows HTTP.
- The security group inbound rules are checked: HTTP 80 from
0.0.0.0/0is allowed. - Nginx answers. The reply goes to the browser's ephemeral port (a random port, 1024–65535 is the range AWS recommends to allow). The security group lets it out automatically because it remembers the connection (stateful).
- The NACL outbound rules are checked again (stateless): you need a rule allowing TCP 1024–65535 to
0.0.0.0/0, otherwise the reply is dropped and the browser waits.
What does each one look like in a real example?
Example 1 — A web server security group (stateful, allow only)
| Type | Port | Source |
|---|---|---|
| HTTP | 80 | 0.0.0.0/0 |
| HTTPS | 443 | 0.0.0.0/0 |
| SSH | 22 | bastion-sg (another security group) |
No outbound rule is needed for replies. Nothing else can come in.
Example 2 — A custom NACL for the same public subnet (stateless, ordered)
| Direction | Rule | Type | Port | Source/Destination | Action |
|---|---|---|---|---|---|
| Inbound | 90 | All traffic | All | 203.0.113.25/32 |
Deny |
| Inbound | 100 | HTTP | 80 | 0.0.0.0/0 |
Allow |
| Inbound | 110 | HTTPS | 443 | 0.0.0.0/0 |
Allow |
| Inbound | 120 | SSH | 22 | 198.51.100.7/32 (your IP) |
Allow |
| Inbound | 130 | Custom TCP | 1024–65535 | 0.0.0.0/0 |
Allow (replies to the server's own outgoing requests, e.g. yum) |
| Outbound | 100 | HTTP | 80 | 0.0.0.0/0 |
Allow |
| Outbound | 110 | HTTPS | 443 | 0.0.0.0/0 |
Allow |
| Outbound | 120 | Custom TCP | 1024–65535 | 0.0.0.0/0 |
Allow (replies to visitors) |
| Both | * |
All | All | 0.0.0.0/0 |
Deny |
The documentation addresses 203.0.113.25 and 198.51.100.7 stand in for a real attacker and your real IP.
Example 3 — Security group chaining (only an SG can do this)
db-sg allows MySQL 3306 with source app-sg. Any instance that has app-sg can connect, even when its IP changes, and nothing else can. A NACL can only use CIDR ranges.
Which one should I use?
- Always use security groups: they are your main, fine-grained firewall per instance.
- Keep the default NACL (allow all) unless you need a subnet-wide rule.
- Add a NACL deny to block an IP or range: How to block an IP address or range using NACL.
- Use both in regulated or large setups as defence in depth: a mistake in one layer is caught by the other. 🛡️
Ravindra Bagale's Tip
The interview favourite: "Security group vs NACL". Remember four words – level (instance vs subnet), state (stateful vs stateless), rules (allow vs allow+deny), order (all vs by number). And give one example: "To block an IP, use the NACL, because an SG cannot deny." The interviewer is happy!
Ravindra Bagale's Tip – मराठी
Interview चा आवडता प्रश्न: "Security group vs NACL". चार शब्द लक्षात ठेवा – level (instance vs subnet), state (stateful vs stateless), rules (allow vs allow+deny), order (सगळे vs number प्रमाणे). आणि एक example द्या: "IP block करायचा असेल तर NACL, कारण SG deny करू शकत नाही." Interviewer खुश!
Ravindra Bagale's Tip – हिंदी
Interview का पसंदीदा सवाल: "Security group vs NACL". चार शब्द याद रखो – level (instance vs subnet), state (stateful vs stateless), rules (allow vs allow+deny), order (सारे vs number के हिसाब से). और एक example दो: "IP block करना हो तो NACL, क्योंकि SG deny नहीं कर सकता." Interviewer खुश!
What goes wrong when mixing them?
Ghabru naka 😅 — these are the usual ones:
| Symptom | Likely cause | Fix |
|---|---|---|
| SG allows port 80 but the site times out | Custom NACL has no inbound 80 or no outbound 1024–65535 | Add both NACL rules |
yum hangs on a server in a custom-NACL subnet |
Inbound 1024–65535 missing (replies to the server's own requests) | Add NACL inbound 1024–65535 |
| Trying to add "Deny" in a security group | Security groups have no deny | Use a NACL deny rule |
| NACL allow rule added but still blocked | A lower-numbered deny matches first | Check the order from the lowest number |
Rule with source app-sg not possible in NACL |
NACLs accept CIDR only | Use the subnet CIDR (10.0.2.0/24) in the NACL, the SG ID in the security group |
Try it at home
Create a custom NACL for a test subnet with only inbound HTTP 80 allowed (no outbound rules), associate it and try to open the site: it hangs. Add outbound 1024–65535 and it works. Write in your own words why the security group never needed that rule.
Learn it properly
Got it? Security group – instance, stateful, allow only. NACL – subnet, stateless, allow and deny, by number. Everyday work with the security group, blocking an IP with the NACL.
समजलं का? सिक्युरिटी ग्रुप – इन्स्टन्स, स्टेटफुल, फक्त अलाउ. NACL – सबनेट, स्टेटलेस, अलाउ आणि डिनाय, नंबर प्रमाणे. रोजचं काम सिक्युरिटी ग्रुप ने, IP ब्लॉक NACL ने.
समझ आया? सिक्योरिटी ग्रुप – इंस्टेंस, स्टेटफुल, सिर्फ अलाउ. NACL – सबनेट, स्टेटलेस, अलाउ और डिनाय, नंबर के हिसाब से. रोज़ का काम सिक्योरिटी ग्रुप से, IP ब्लॉक NACL से.
Frequently asked questions
What is the main difference between a security group and a NACL?
A security group is a stateful, allow-only firewall for an instance. A network ACL is a stateless firewall for a subnet with numbered allow and deny rules.
What does stateful mean for a security group?
If a request is allowed in, its reply is allowed out automatically, and the other way round. You do not write rules for return traffic.
Why do NACLs need ephemeral ports?
Because they are stateless: the reply to a visitor goes to the visitor's random port, so an outbound rule for TCP 1024-65535 is needed, and an inbound one for replies to the server's own requests.
Can I use a security group as the source in a NACL?
No. Network ACL rules accept CIDR ranges only. Referencing another security group, like app-sg, works only in security groups.
Which is checked first, the NACL or the security group?
For traffic entering a subnet, the network ACL is checked at the subnet edge first, then the security group at the instance. Both must allow it.
Should I change the default NACL?
Usually not. The default network ACL allows all traffic and security groups do the fine-grained work. Add NACL rules when you need to deny addresses or want a second layer.