A VPC gives you two firewalls, and they are not redundant. Security groups are stateful and wrap individual instances; network ACLs are stateless and guard whole subnets. Confusing the two is the source of most "why is my traffic blocked" incidents.
This page shows both from the SDK, side by side, and makes the stateful-vs-stateless distinction concrete with code you can reason about.
The common case is a security group: default-deny, with the specific inbound ports you allow. Because security groups are stateful, you only declare the inbound rule - the response is allowed automatically.
A new security group has no inbound rules (deny all in) and one default outbound rule allowing all traffic out. You add the inbound ports you need; the stateful engine handles the return path.
Reference another security group as the source instead of a CIDR. This is how you say "the web tier may reach the database on 5432" without hardcoding any IP - the rule follows the instances, not their addresses.
UserIdGroupPairs references another security group as the source. Any instance in web-sg can now reach any instance in db-sg on 5432, and instances added later inherit the rule automatically. To remove a rule, RevokeSecurityGroupIngress with the identical permission shape.
Stateful (security group): when an instance allows inbound TCP 443, the response from the instance back to the client is allowed automatically, because the security group tracks the connection. You never write a matching outbound rule for the return traffic.
Stateless (network ACL): the NACL evaluates every packet independently with no memory of connections. If you allow inbound 443, the outbound response is not automatically allowed - you must add an outbound rule for it. And because the client's side of a TCP connection uses a random high port, that outbound rule has to allow the whole ephemeral port range (1024-65535), not port 443.
This ephemeral-port requirement is the number-one NACL gotcha. Forget it and connections establish inbound but the reply is dropped.
NACLs attach to subnets and use numbered rules evaluated lowest-first; the first match wins. Ingress and egress are separate rule sets, so a working web NACL needs both.
Protocol is the IANA number as a string ("6" TCP, "17" UDP, "-1" all). Egress selects the direction. Rules are numbered so you can leave gaps (100, 200) and insert later. Unlike security groups, NACLs support explicit deny rules, which is why they are useful for blocking a specific bad CIDR.
Because both layers apply - NACL at the subnet edge, security group at the ENI - a dropped packet could be either. The rule of thumb: security groups are stateful and default-deny inbound, so a missing SG rule is the usual culprit; if the SG looks right, check the NACL's ephemeral egress rules next. VPC Flow Logs (next page) record ACCEPT/REJECT and help pinpoint the layer.
Security groups alone are enough for most workloads; many teams leave the default NACL wide open and rely entirely on stateful SG rules.
Network ACLs add value as a coarse, stateless backstop - blocking a known-bad CIDR subnet-wide, or enforcing a hard boundary that a misconfigured SG cannot punch through.
AWS Network Firewall provides deep-packet, managed stateful inspection at the VPC edge when SG/NACL rules are too coarse.
AWS WAF filters HTTP(S) at the application layer (on ALB/CloudFront), which SGs and NACLs cannot inspect.
Use security groups as your primary control, add NACLs only where a subnet-wide stateless rule earns its complexity, and escalate to Network Firewall or WAF when you need layer-7 or deep inspection.
What is the core difference between a security group and a network ACL?
A security group is stateful and attaches to network interfaces; return traffic is automatic. A network ACL is stateless and attaches to subnets; you must allow each direction explicitly.
Why do I need to allow ephemeral ports on a NACL?
Because it is stateless, the return leg of a connection uses a random high port (1024-65535) and is evaluated independently. Without an outbound rule for that range, replies are dropped.
Do I need outbound rules on a security group for responses?
No. Security groups are stateful, so responses to allowed inbound traffic are permitted automatically regardless of egress rules.
Can a security group rule reference another security group?
Yes, via UserIdGroupPairs. The rule then applies to any instance in the referenced group, so it follows instances rather than IP addresses.
Can a network ACL explicitly deny traffic?
Yes. NACL entries support allow and deny, so they can block a specific CIDR. Security groups only allow - there is no deny rule.
How are NACL rules evaluated?
By rule number, lowest first, and the first match wins for that direction. Ingress and egress are separate numbered rule sets.
What are the defaults for a new security group?
No inbound rules (all inbound denied) and one outbound rule allowing all traffic. You add the specific inbound ports you need.
How do I remove a security group rule?
RevokeSecurityGroupIngress or RevokeSecurityGroupEgress with a permission shape that exactly matches the existing rule.
If traffic is blocked, which layer do I check first?
The security group, since it defaults to deny-inbound. If it looks correct, check the NACL - most often a missing ephemeral egress rule. Flow Logs confirm which layer rejected.