AWS gives you two firewalls inside a VPC: security groups and network access control lists (NACLs). They look similar in the console, both have inbound and outbound rules, and both can stop your traffic cold. But they work very differently, and mixing up their behavior is one of the most common causes of mysterious timeouts.
The short version
| Security group | Network ACL | |
|---|---|---|
| Attached to | Network interface (instance, ALB, RDS, Lambda ENI) | Subnet |
| Rule types | Allow only | Allow and deny |
| State | Stateful: replies are allowed automatically | Stateless: replies must be allowed explicitly |
| Evaluation | All rules are evaluated together | Rules evaluated in number order, first match wins |
| Default | New SG: deny all inbound, allow all outbound | Default NACL: allow all both ways |
| Can reference | Other security groups | CIDR ranges only |
Security groups: stateful and allow-only
A security group is a firewall around each network interface. Its rules only say what is allowed; anything not allowed is denied. Because it is stateful, if a request is allowed in, the response is automatically allowed out, and vice versa. You never need to think about return ports.
The most powerful feature is that a rule can reference another security group instead of an IP range. For a three-tier app:
alb-sg: inbound 443 from0.0.0.0/0.app-sg: inbound 8080 fromalb-sg.db-sg: inbound 3306 fromapp-sg.
Now any instance in the app tier can reach the database, no matter its IP, and nothing else can. When Auto Scaling replaces instances, the rules keep working because they follow membership, not addresses.
Network ACLs: stateless and ordered
A NACL guards the subnet boundary. Every packet entering or leaving the subnet is checked against the rules in order of their rule number, lowest first, and the first matching rule decides. If nothing matches, the final * rule denies.
Because NACLs are stateless, a request and its response are evaluated separately. That is where the trouble starts.
The ephemeral port trap
When a client connects to your web server on port 443, the client picks a random source port, typically in the range 1024–65535 (the exact range depends on the operating system). Your server replies to that random port. With a security group, the reply is allowed automatically. With a NACL, the subnet's outbound rules must allow traffic to those ephemeral ports, or the reply is dropped and the client sees a timeout.
A correct NACL for a public web subnet therefore looks like this:
Inbound
100 TCP 443 0.0.0.0/0 ALLOW
110 TCP 1024-65535 0.0.0.0/0 ALLOW (replies to outbound calls)
* ALL 0.0.0.0/0 DENY
Outbound
100 TCP 1024-65535 0.0.0.0/0 ALLOW (replies to clients)
110 TCP 443 0.0.0.0/0 ALLOW (outbound HTTPS calls)
* ALL 0.0.0.0/0 DENY
Rule order matters
Because the first match wins, a broad allow at rule 100 makes a specific deny at rule 200 useless. Put specific denies first (for example rule 50 denying a known-bad IP range), then the broader allows.
When to use which
- Use security groups for almost all access control. They are precise, follow your resources and are easier to reason about.
- Use NACLs as a coarse second layer: blocking a specific IP range at the subnet edge, enforcing that a database subnet never talks to the internet, or meeting a compliance rule that asks for subnet-level controls.
- Leave the default NACL as "allow all" unless you have a reason to change it. Many outages start with a well-meant NACL "hardening" that forgot ephemeral ports.
Troubleshooting checklist for blocked traffic
- Timeout or refused? "Connection refused" usually means the packet arrived and nothing listens on that port. A timeout points to a firewall or routing problem.
- Security group of the target: is the port open from the source's IP or security group?
- Security group of the source: is outbound traffic allowed? (It is by default, but teams often restrict it.)
- NACLs on both subnets: check inbound and outbound, including ephemeral return ports.
- Route tables: does each side have a route to the other (local, peering, transit gateway, NAT or internet gateway)?
- VPC Reachability Analyzer: it traces the path between two endpoints and tells you exactly which component blocks it.
- VPC Flow Logs:
REJECTrecords show which side dropped the packet.
Practice it
The free Cloud Arena lab Security Group vs NACL lets you break and fix both controls in a real AWS account. For a realistic outage, try the incident labs Menu Site Blocked by a Network ACL and Status Page Times Out After the NACL Hardening: each starts broken, and you find and fix the cause.
FAQ
Can a security group deny traffic?
Not explicitly. It only has allow rules; anything not allowed is denied. To block a specific IP while allowing everyone else, use a NACL deny rule (or AWS WAF for HTTP traffic).
Do NACLs apply to traffic inside the same subnet?
No. NACLs only evaluate traffic crossing the subnet boundary. Two instances in the same subnet are only controlled by their security groups.
How many security groups can an instance have?
By default up to five per network interface, and the effective permission is the union of all their rules.