Back to VPC & Networking
Q3Question 3 of 8

Security Group versus Network ACL

Key Concepts
  • A Security Group is a stateful, instance-level firewall that supports allow rules only and automatically permits return traffic.
  • A NACL is a stateless, subnet-level firewall that supports both allow and deny rules, evaluated in numbered order.
  • Stateful means the Security Group remembers connections, so you only need an inbound rule for the response to flow back.
  • Stateless means the NACL evaluates each packet independently, requiring explicit inbound and outbound rules including ephemeral ports.
  • Security Group rules can reference other Security Groups by ID, avoiding hardcoded IP addresses.
  • Use Security Groups as the primary fine-grained control on every instance; use NACLs as a coarse subnet-wide guardrail for blanket denies.
  • Forgetting the outbound ephemeral port rule on a NACL is the classic cause of connections that hang silently.

Definition

Security Group is a stateful allow-only firewall on the instance that remembers return traffic, while a NACL is a stateless allow-and-deny firewall on the subnet that treats each direction separately.
  • Security Group – A stateful, instance-level firewall that supports allow rules only; return traffic for an allowed connection is automatically permitted.
  • Network ACL (NACL) – A stateless, subnet-level firewall that supports both allow and deny rules, evaluated in numbered order with first-match-wins logic.
  • Stateful – The firewall tracks connection state, so an allowed inbound request automatically allows the response outbound.
  • Stateless – Each packet is evaluated independently with no memory of the connection, requiring explicit rules for both directions.
  • Ephemeral ports – The high-numbered ports (1024-65535) used for return traffic, which must be explicitly allowed in NACL outbound rules.

A Security Group is like the personal guard standing at Rajesh's office door at National Bank of India – it remembers that Rajesh went out, so it lets his reply back in automatically, and it only has a list of who is allowed in, never a blacklist. A NACL is like the guard at the entrance to the whole floor – it does not remember anyone, so you must tell it both let visitors in and let replies out, and it can keep both an allow list and a block list, checking them in a fixed numbered order.

Understanding the Concepts

What is a Security Group?

A Security Group attaches to an elastic network interface, effectively to an instance. It is stateful, which means if you allow an inbound request, the response is automatically allowed out without a matching outbound rule, and vice versa. It supports allow rules only – there is no way to write a deny rule in a Security Group; anything not explicitly allowed is simply denied. All rules are evaluated together as a set; order does not matter.

TEXT
# Security Group for the settlement service (stateful, allow-only)
Inbound:
  allow tcp 8080 from sg-alb          (traffic from the load balancer group)
  allow tcp 22   from 10.0.0.0/16     (SSH from the corporate network)
Outbound:
  allow all                            (return traffic handled automatically)
# No deny rules possible. Anything not allowed is implicitly denied.

What is a Network ACL?

A NACL attaches to a subnet and applies to every resource in that subnet. It is stateless, so each packet is evaluated independently with no memory of prior packets. This means for a TCP connection you must allow the inbound request and separately allow the outbound response, typically on the ephemeral port range. A NACL supports both allow and deny rules, and rules are numbered; evaluation stops at the first rule that matches, lowest number first.

TEXT
# NACL for private subnet (stateless, numbered, allow+deny)
Inbound:
  100 allow tcp 8080  from 10.0.0.0/16          (app traffic)
  200 deny  all       from 203.0.113.0/24       (block known bad range)
  *   deny  all                                  (default deny)
Outbound:
  100 allow tcp 1024-65535 to 10.0.0.0/16       (ephemeral return ports)
  *   deny  all                                  (default deny)

How is stateful different from stateless?

| Aspect | Stateful (Security Group) | Stateless (NACL) |
|---|---|---|
| Connection tracking | Remembers the connection | Each packet evaluated independently |
| Return traffic | Automatically allowed | Must be explicitly allowed |
| Inbound rule only? | Yes, response flows back automatically | No, you need outbound rule for response |
| Ephemeral ports | Not your concern | Must allow 1024-65535 outbound |
| Classic mistake | N/A | Forget outbound ephemeral = connection hangs |

JAVA
public class StatefulVsStateless {
    // Security Group: allow inbound 8080 → response on ephemeral port auto-allowed
    // NACL: allow inbound 8080 → ALSO need outbound 1024-65535 explicitly
    // Miss the outbound = packets arrive but replies are dropped = silent hang
}

How do Security Group rules reference other groups?

Rules can reference other Security Groups by ID, which is powerful. Instead of hardcoding IP addresses, you say allow inbound from any instance in the app-tier Security Group. This is cleaner than IP ranges because as instances scale up or down, the rule automatically applies to all members of the referenced group.

JSON
{
  "IpPermissions": [{
    "IpProtocol": "tcp",
    "FromPort": 5432,
    "ToPort": 5432,
    "UserIdGroupPairs": [{
      "GroupId": "sg-settlement-service",
      "Description": "Allow PostgreSQL from settlement service only"
    }]
  }]
}

When do you use each?

The Security Group is your primary tool and does almost all the work. You use it on every instance to control exactly who can reach it. It handles fine-grained, per-instance access control.

  • The NACL is a secondary, coarse layer at the subnet edge. It is useful for things a Security Group cannot do:
  • Block a known-bad IP range across the entire subnet (deny rules).
  • Add defense in depth around a sensitive database subnet.
  • Enforce a subnet-wide policy that individual instance owners cannot override.
JAVA
public class WhenToUseEach {
    // Security Group: primary tool, fine-grained, per-instance
    // Example: settlement service allows 8080 only from the ALB group

    // NACL: secondary tool, coarse, per-subnet
    // Example: block a known attacker range for the entire private subnet
    // Example: defense in depth around the database subnet
}

What are common mistakes?

The first and most common mistake is forgetting the outbound ephemeral port rule on a NACL. Because NACLs are stateless, if you allow inbound port 8080 but forget outbound 1024-65535, the response is dropped and the connection hangs silently.

The second mistake is trying to deny a specific IP in a Security Group. Security Groups only support allow rules. You need a NACL to deny.

The third mistake is opening all traffic in the NACL's default rules and relying only on Security Groups. This removes a layer of defense.

DIAGRAM
# WRONG: NACL with inbound allow but missing outbound ephemeral
Inbound:  100 allow tcp 8080 from 10.0.0.0/16
Outbound: *   deny all                          ← blocks all responses!
# Result: request arrives, response dropped, connection hangs forever

# CORRECT: include outbound ephemeral
Inbound:  100 allow tcp 8080 from 10.0.0.0/16
Outbound: 100 allow tcp 1024-65535 to 10.0.0.0/16  ← responses flow back

How do Security Groups and NACLs work together?

Traffic must pass through both layers. An inbound packet first hits the NACL at the subnet boundary, then the Security Group at the instance. An outbound packet first hits the Security Group, then the NACL. Both must allow the traffic for it to flow.

This layered model is defense in depth. The Security Group handles fine-grained access, and the NACL acts as a coarse outer boundary.

DIAGRAM
Inbound packet path:
  Internet → NACL (subnet) → Security Group (instance) → Application

Outbound packet path:
  Application → Security Group (instance) → NACL (subnet) → Internet

Both layers must ALLOW for traffic to flow.
Either layer can BLOCK: NACL with deny, SG with implicit deny.

What should a senior engineer know?

A senior engineer leans heavily on Security Groups for fine-grained instance control and keeps NACLs simple as a coarse subnet-wide guardrail. They know that Security Groups can have up to 60 inbound and 60 outbound rules per group, and an instance can have up to 5 groups, giving 300 rules per interface. For complex architectures they use prefix lists to group CIDRs.

They also know that NACLs are processed before Security Groups on inbound traffic, so a NACL deny stops traffic before the Security Group ever sees it, which is useful for incident response when you need to immediately block a malicious IP across an entire subnet.

BASH
# Incident response: immediately block an attacker IP at the NACL
aws ec2 create-network-acl-entry \
  --network-acl-id acl-0abc \
  --rule-number 50 \
  --protocol -1 \
  --cidr-block 203.0.113.50/32 \
  --rule-action deny \
  --ingress
# Blocks this IP for EVERY resource in the subnet, immediately

Visual Overview

DIAGRAM
┌──────── Security Group vs NACL ────────┐
│                                         │
│  Subnet Boundary (NACL):                │
│  ┌─────────────────────────────────┐    │
│  │ Stateless, numbered rules       │    │
│  │ Allow + Deny                    │    │
│  │ Applies to ALL instances in     │    │
│  │ the subnet                      │    │
│  │                                 │    │
│  │  Inbound:                       │    │
│  │  100 Allow tcp 8080 from VPC    │    │
│  │  200 Deny  all from bad range   │    │
│  │  Outbound:                      │    │
│  │  100 Allow tcp 1024-65535       │    │
│  └──────────────┬──────────────────┘    │
│                 │                        │
│                 ▼                        │
│  Instance Level (Security Group):        │
│  ┌─────────────────────────────────┐    │
│  │ Stateful, allow-only            │    │
│  │ Return traffic automatic        │    │
│  │ Per-instance (ENI)              │    │
│  │                                 │    │
│  │  Inbound:                       │    │
│  │  Allow tcp 8080 from sg-alb     │    │
│  │  Allow tcp 22 from corporate    │    │
│  │  (No deny rules possible)       │    │
│  └─────────────────────────────────┘    │
│                                         │
│  Both must ALLOW for traffic to flow    │
└─────────────────────────────────────────┘

Key Takeaway

A Security Group is a stateful, instance-level firewall that supports allow rules only. Stateful means that if I allow an inbound request, the response is automatically allowed back out without me writing a matching outbound rule, and allow-only means there is no concept of a deny rule – anything I have not explicitly allowed is denied by default. Its rules are evaluated as a set, so order does not matter, and they can reference other Security Groups, so I can say allow traffic from anything in the load-balancer group without hardcoding IP addresses.

A Network ACL is a stateless, subnet-level firewall that supports both allow and deny rules. Stateless means each packet is judged on its own with no memory of the connection, so for a TCP conversation I have to allow the inbound request and separately allow the outbound response on the ephemeral port range – forgetting that return rule is the classic cause of connections where the request lands but the reply vanishes. Its rules are numbered and evaluated lowest number first, stopping at the first match.

The way I decide when to use each is simple. The Security Group is my primary tool and does almost all the real work – I put one on every instance to control precisely who can reach it, and its stateful nature keeps the rules clean. The NACL is a secondary, coarse outer layer at the subnet boundary, which I reach for when I need something a Security Group cannot express, like a blanket deny of a known-malicious IP range for every resource in the subnet, or defense in depth around a sensitive database subnet.

At National Bank of India our settlement service sits behind a Security Group that allows port 8080 only from the load balancer group and SSH only from the corporate network, with return traffic handled automatically because the group is stateful. When a known attacker range started probing us, we added a deny rule in the subnet's NACL to block that range for the whole subnet at once, which a Security Group simply cannot do.

Remember one sentence: Security Groups are stateful allow-only firewalls per instance, NACLs are stateless allow-and-deny firewalls per subnet, and you need both for layered defense.

---

Share
Use ← → arrow keys to navigate