Hands-On: Security Groups and NACLs in Action

Watching traffic get blocked and unblocked in real time is what actually makes Security Groups and NACLs click. This post walks through a practical setup - a custom VPC, an EC2 instance running a Python HTTP server, and live manipulation of Security Group and NACL rules.
The Setup
Custom VPC (10.0.0.0/16)
- Public Subnet
- EC2 Instance (Python HTTP server on port 8000)
- Security Group (inbound rules)
- NACL (subnet-level rules)
Next is an EC2 instance, we've to configure it's VPC to the one we've created.
SSH and fire the following commands to initiate a python HTTP server
sudo apt update
python3 -m http.server 8000
Case1: Default Security Group Blocks Everything
Hit http://65.2.191.13:8000 in the browser. Nothing.
Expected - the default Security Group AWS creates only allows port 22 (SSH). Port 8000 isn't in there. The NACL at this point is default allow-all, so the block is entirely coming from the Security Group.
Clean. One checkpoint, one rule, done.
Case 2: NACL Overrides the Security Group
This is where it gets interesting.
Security Group still allows port 8000. I went into the NACL and set up these inbound rules:
| Rule # | Type | Port | Source | Action |
|---|---|---|---|---|
| 100 | Custom TCP | 8000 | 0.0.0.0/0 | DENY |
| 200 | All traffic | All | 0.0.0.0/0 | ALLOW |
Refreshed the browser. Application GONE! The Security Group hadn't changed. Port 8000 was still explicitly allowed at the instance level. Didn't matter - NACL rule 100 killed it at the subnet boundary.
Round 3: Flipped the Rule Order
| Rule # | Type | Port | Source | Action |
|---|---|---|---|---|
| 100 | All traffic | All | 0.0.0.0/0 | ALLOW |
| 200 | Custom TCP | 8000 | 0.0.0.0/0 | DENY |
Application came back up immediately.
Rule 100 matched first (Allow All), evaluation stopped, rule 200 never ran.
This is a subtle thing that would be genuinely painful to debug if you didn't know how NACL evaluation works.
What Stuck With Me
The Security Group and NACL interaction is clean in theory but the stateless nature of NACLs is easy to forget in practice. If I were doing this in a real setup I'd also need outbound rules for responses to leave the subnet - something I skipped here since I was testing inbound blocking behavior, not response flow.
Worth breaking in a test VPC before it breaks something that matters.






