Ravindra BagaleCourses & study guides Track your progress

Guides

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.

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?

Security group vs network ACL: where each one checks traffic A request first meets the network ACL at the subnet edge, which checks its numbered inbound rules. Then it meets the security group around the instance, which checks its allow rules. The reply goes out through the security group automatically because it is stateful, but the network ACL is stateless and checks the reply again against its outbound rules, so ports 1024-65535 must be allowed outbound. User request :80reply to port 1024–65535 Subnet NACL (subnet) Security group EC2 inbound rule 100 ✓ allow 80 ✓ stateful: auto stateless: outbound rule NACL checks both directions (stateless) · SG remembers the connection (stateful) req reply

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.

  1. A browser sends a request to port 80. The NACL inbound rules are checked in order: rule 100 allows HTTP.
  2. The security group inbound rules are checked: HTTP 80 from 0.0.0.0/0 is allowed.
  3. 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).
  4. 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!

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.

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.

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.