~13 min
A VPC is your own isolated network inside AWS, split into subnets. A public subnet has a route to an internet gateway; a private subnet does not. Two independent controls filter traffic inside that network.
A security group is stateful and operates at the resource level (an EC2 instance, an RDS database, a load balancer). It only supports allow rules — if you send a request out, the response is let back in automatically, regardless of your inbound rules. A network ACL (NACL) is stateless and operates at the subnet level, covering every resource in that subnet whether you meant it to or not. NACLs support both allow and deny rules, and they evaluate their numbered rules in order — lowest number first — stopping at the first match. Because a NACL is stateless, allowing inbound traffic on a port does not automatically allow the outbound response; you have to write that rule yourself.
bash
aws ec2 authorize-security-group-ingress \
--group-id sg-0123456789abcdef0 \
--protocol tcp --port 443 \
--source-group sg-0fedcba9876543210Two AWS services add protection above the network layer. AWS WAF is a web application firewall: it inspects the content of HTTP(S) requests reaching a CloudFront distribution, an Application Load Balancer, an API Gateway REST API, and a few other resource types, and it can allow, block, count, or challenge (CAPTCHA) requests based on rules you write — an IP address, a country, a string in the request, or a request rate.
AWS Shield defends against distributed denial-of-service (DDoS) floods instead. Shield Standard is automatic and included at no extra charge for every AWS customer, and it already covers common network- and transport-layer (layer 3/4) attacks on resources like an ALB, CloudFront, or Route 53. Shield Advanced is a paid subscription that adds application-layer (layer 7) DDoS mitigation, near-real-time attack visibility, and access to the AWS DDoS Response Team — worth it for a high-visibility site that's a frequent target, not for every workload by default.
Two more pieces close the gap between "traffic reached the instance" and "the application is actually secure." First, application credentials: hardcoding a database password in source code means anyone with read access to that code has the password. AWS Secrets Manager stores it instead, and can rotate it automatically on a schedule using a small Lambda function — the application always asks Secrets Manager for the current value rather than embedding one.
Second, how private instances reach other AWS services without touching the public internet. A gateway VPC endpoint (for Amazon S3 and DynamoDB only) adds a route to those services through your subnet's route table, at no extra charge. A VPC interface endpoint, built on AWS PrivateLink, gives a private IP address inside your VPC for most other AWS services, so a private subnet can reach them without a NAT gateway or any route to the internet.
The deny at rule 100 wins — NACL rules are evaluated in order by rule number, and evaluation stops at the first match.