Web applications on EC2 are everywhere. And honestly, a lot of them are just sitting there exposed to the internet with nothing more than a Security Group between them and every bot, scanner, and attacker out there.
This article walks through building a straightforward architecture to answer one specific question: How do you protect a web application on EC2 from common web attacks without touching the application code?
We'll use AWS WAF for Layer 7 protection, Security Groups for network control, and AWS Certificate Manager for encrypted transport, all at minimal cost and complexity.
Understanding network layers before adding security controls
Before jumping into AWS services, let's get clear on how network communication actually works. The OSI model helps here, but we'll keep it practical.
Layer 3 – Network layer
Deals with IP addresses. This is about knowing where traffic is coming from and where it's headed.
Layer 4 – Transport layer
Deals with ports and protocols. This controls how connections get established, think TCP on port 80 or 443.
Together, these two layers answer questions like: Who's sending this traffic? Which port are they using? Should this connection even be allowed?
Layer 7 – Application layer
This is where HTTP and HTTPS live. It understands URLs, headers, query strings, request bodies and the actual content of what's being sent.
Layer 7 answers a different kind of question: What is the user actually trying to do?
Here's the thing: most web attacks happen at Layer 7, not at the network level. Understanding this distinction is critical.
What a Security Group is and how it works
Security Groups are probably the first security control you run into in AWS. They're virtual firewalls that control network level access to things like EC2 instances and load balancers.
A Security Group evaluates traffic based on:
- Source IP address
- Destination port
- Protocol
From the OSI perspective, Security Groups operate at Layer 3 (Network) and Layer 4 (Transport).
Key characteristics:
- Stateful – if traffic is allowed in, the response is automatically allowed out
- Allow only – you can't write deny rules, only allow rules
- Everything else is denied by default
Example rules:
- Allow inbound TCP traffic on port 80 from anywhere (0.0.0.0/0)
- Allow SSH on port 22 only from your office IP
- Deny everything else (implicit)
Security Groups are essential. They're your first line of defense.
What Security Groups cannot protect against
But here's the limitation: Security Groups don't understand HTTP or HTTPS content at all.
They can't:
- Inspect URLs
- Analyze query strings
- Detect SQL injection attempts
- Catch Cross Site Scripting (XSS)
- Identify malicious payloads in request bodies
From a Security Group's perspective, if port 80 is open and the source IP is allowed, the request gets through no matter what's actually inside that request.
This is a fundamental limitation when you're trying to protect web applications.
Why application layer protection is required
Web attacks are application layer problems. That means you need Layer 7 inspection.
This is where AWS WAF comes in.
AWS WAF is a fully managed Web Application Firewall that inspects HTTP and HTTPS requests before they ever reach your application. It looks at:
- URL paths
- Headers
- Query strings
- Request bodies
Based on rules you define (or use AWS Managed Rules), it can:
- Allow requests
- Block requests
- Count requests for monitoring
The best part? No agents on your EC2 instances. No changes to your application code.
Why an Application Load Balancer is part of the design:
AWS WAF can't attach directly to an EC2 instance. It needs to be associated with one of these:
- Application Load Balancer
- API Gateway
- CloudFront
For this architecture, the Application Load Balancer serves as:
- The public entry point
- The integration point for AWS WAF
- The boundary between the internet and your compute layer
Even if you're only running a single EC2 instance, this design mirrors what you'd see in production environments.
Architecture overview
The architecture below shows the scenario we are going to discuss in this article.

Figure 1: Conceptual architecture for protecting an EC2-hosted web application using AWS WAF attached to an internet-facing Application Load Balancer
The flow works like this:
- Internet traffic hits the Application Load Balancer
- AWS WAF inspects HTTP requests at Layer 7
- Allowed requests get forwarded to the EC2 instance
- Security Groups restrict network level access between components
Architecture components:
Amazon EC2 – Runs a simple Nginx web application in a public subnet
Application Load Balancer – Exposes the application to the internet and routes traffic to EC2
AWS WAF – Inspects and filters HTTP requests before they reach the application
AWS Certificate Manager – Provides free SSL/TLS certificates for HTTPS encryption
Systems Manager Session Manager – Provides secure, SSH free access to EC2 instances via IAM
Auto Scaling Group – Maintains a single EC2 instance (and makes it easy to scale later if needed)
Security Groups – Enforce strict Layer 3 and Layer 4 access rules
Each component protects a specific layer. There's no redundancy or overlap.
Setting up the infrastructure
Let's walk through the key configuration steps. I'll focus on the security specific settings that matter most.
Step 1: Creating the Application Load Balancer
First, we need to set up the ALB that will serve as our public entry point.

Figure 2: Application Load Balancer basic configuration
Key settings to configure:
- Scheme: Internet facing
- IP address type: IPv4
- Listeners: HTTP (port 80) and HTTPS (port 443)
- Availability Zones: Select at least two for high availability
The ALB needs to be in a public subnet since it's receiving traffic directly from the internet.
Step 2: Configuring Security Groups
Security Groups control network level access. We need two distinct groups here.
ALB Security Group:

Figure 3: Security Group rules for the Application Load Balancer
Inbound rules:
- Allow HTTP (port 80) from 0.0.0.0/0
- Allow HTTPS (port 443) from 0.0.0.0/0
EC2 Security Group:

Figure 4: Security Group rules for the EC2 instance
Inbound rules:
- Allow HTTP (port 80) only from the ALB Security Group
Note: Unlike traditional setups, we're not opening SSH port 22. Access to the instance for management purposes is handled through AWS Systems Manager Session Manager, which uses IAM authentication and doesn't require any inbound ports to be open.
This is crucial: the EC2 instance never accepts traffic directly from the internet. Only the ALB can reach it on port 80, and administrative access is through SSM Session Manager only.
Step 3: Creating the AWS WAF Web ACL
Now we add the Layer 7 protection. This is where AWS WAF comes in.

Figure 5: Creating a Web ACL in AWS WAF
When creating the Web ACL:
- Give it a descriptive name (e.g.,
ec2-app-protection-waf) - Select Application Load Balancer as the resource type
- Associate it with the ALB you created earlier
Step 4: Adding AWS Managed Rules
Instead of writing custom rules from scratch, we'll use AWS Managed Rule Groups. These are maintained by AWS and updated automatically.
Recommended rule groups to enable:
- Core rule set (CRS) – Protects against common attacks like OWASP Top 10
- Known bad inputs – Blocks requests with patterns associated with exploit attempts
- SQL database – Prevents SQL injection attacks
- Linux operating system – Blocks requests targeting common Linux vulnerabilities
Each rule group adds a layer of protection. Start with these four since they cover the most common attack vectors.
Step 5: Associating WAF with the ALB
Once the Web ACL is configured, associate it with your Application Load Balancer.
The association happens in one of two ways, depending on where you started the configuration. If you created the Web ACL from the WAF console, you selected the ALB during the initial setup under (Associated AWS resources). If you're adding WAF to an existing ALB, you'll find the option in the Load Balancer console under the (Integrated services) tab.
From the Load Balancer perspective, you'll see a section labeled (AWS WAF) where you can attach a Web ACL. Click "Edit" and select your newly created Web ACL from the dropdown. The attachment is immediate there's no downtime, no need to restart anything.
Once associated, you'll see the Web ACL name displayed in the ALB's integrated services section. You can verify the reverse connection by going back to the WAF console, selecting your Web ACL, and checking the (Associated AWS resources) tab. Your ALB should be listed there.
This is the critical connection point. Every HTTP request hitting your ALB will now pass through AWS WAF inspection before reaching your EC2 instance. The WAF evaluates each request against your rule groups in order of priority, and either allows it through, blocks it with a 403 response, or counts it for monitoring purposes depending on how you've configured each rule.
The inspection happens inline with minimal latency, typically adding only single digit milliseconds to request processing time. If WAF blocks a request, the connection never reaches your application layer. The request is terminated at the ALB with a 403 Forbidden response.
Step 6: Configuring IAM Role for Systems Manager
To enable secure, SSH free access to the EC2 instance, we attach an IAM role that allows Systems Manager Session Manager to function.

Figure 7: IAM role configuration for Systems Manager access
The IAM role includes the AmazonSSMManagedInstanceCore managed policy, which provides:
- Permission for the SSM agent to communicate with Systems Manager service
- Ability to retrieve commands and send outputs
- CloudWatch Logs integration for session logging (optional but recommended)
Note on SSM Agent: If you're using Amazon Linux 2023 (as in this setup), the SSM agent comes pre installed and enabled by default. For other AMIs like Ubuntu or older Amazon Linux versions, you may need to install the agent manually. The combination of the IAM role and the pre installed agent is what enables Session Manager to work immediately after instance launch.
Once attached, you can access the instance through the AWS console or CLI without opening SSH ports or managing key pairs. All sessions are authenticated through IAM and can be logged for audit purposes.
Step 7: Monitoring and testing
After everything is configured, you can monitor WAF activity through CloudWatch metrics.
AWS WAF provides real time metrics that show how your Web ACL is performing. These metrics are automatically published to CloudWatch and typically appear within 5 to 10 minutes of activity.
Key metrics to watch:
- AllowedRequests: Legitimate traffic getting through
- BlockedRequests: Malicious requests stopped by WAF
- CountedRequests: Requests matching rules in count mode
To view these metrics:
- Navigate to the AWS WAF console
- Select your Web ACL
- Go to the Overview tab
- CloudWatch metrics will display showing request patterns over time
You can also view detailed logs in CloudWatch Logs if you enable WAF logging.
Transport security with HTTPS
All traffic between clients and the Application Load Balancer is encrypted using TLS 1.2 or higher. The SSL certificate is provided by AWS Certificate Manager at no cost and renews automatically.

Figure 10: SSL/TLS certificate from AWS Certificate Manager
The ALB is configured to:
- Accept HTTPS connections on port 443 with a valid SSL certificate
- Redirect all HTTP traffic to HTTPS (301 permanent redirect)
- Use modern cipher suites (TLS 1.2+ only)
- Serve a certificate trusted by all major browsers
This ensures that sensitive data cannot be intercepted or read by attackers performing man-in-the-middle attacks. The certificate is issued and managed by AWS Certificate Manager, which handles automatic renewal before expiration.
Video walkthrough: For a complete hands-on demonstration of this setup, check out the video tutorial below where I walk through each step in the AWS console.
[Video soon]
What AWS WAF protects against in practice
Using AWS Managed Rules, AWS WAF can block common attack patterns like:
- SQL injection attempts
- Cross-Site Scripting (XSS)
- Path traversal attacks
- Malformed or suspicious HTTP requests
- Basic automated scanners and bots
Important point: AWS WAF doesn't fix insecure code. What it does is reduce your exposure by blocking known malicious patterns before they ever reach your application.
What this architecture does NOT cover (and why it matters)
This architecture provides solid foundational protection with network segmentation, application layer filtering, encrypted transport, and IAM based instance access. However, it's important to understand its limitations. This is a strong starting point that demonstrates core security principles, not a complete enterprise grade solution.
What's missing for a production environment:
Network Isolation:
- EC2 in public subnet (production should use private subnet, see "Ideal Architecture" below)
- EC2 has public IP (though no direct access is possible due to Security Groups)
- No VPC Endpoints for fully isolated SSM access
- No NAT Gateway for controlled outbound traffic from private instances
Advanced DDoS Protection:
- No AWS Shield Advanced for sophisticated volumetric attacks
- No rate based rules in WAF to limit request velocity per IP
- No geographic restrictions (geo blocking) for region specific threats
Monitoring and Threat Detection:
- WAF logging disabled (to minimize costs for this demo)
- No AWS GuardDuty for threat intelligence and anomaly detection
- No Security Hub for centralized security findings across services
- No VPC Flow Logs for network traffic analysis
- No CloudWatch Alarms for proactive alerting on suspicious activity
Identity and Secrets Management:
- No AWS Secrets Manager for database credentials or API keys
- Session Manager logging not enabled (optional enhancement)
- No MFA requirement for Session Manager access
Compliance and Governance:
- No AWS Config for continuous compliance checking
- No automated patch management with Systems Manager Patch Manager
- No backup strategy with AWS Backup
- No vulnerability scanning with AWS Inspector
Advanced Application Protection:
- No AWS WAF Bot Control for sophisticated bot mitigation
- No reCAPTCHA challenges for suspicious activity
- No account takeover prevention features
- No fraud detection patterns
Content Delivery:
- No CloudFront for global edge caching and additional DDoS protection
- No origin shielding to reduce load on the ALB
Recommendation: For production, place CloudFront in front of the ALB to:
- Hide the ALB's DNS name from public access
- Add an additional WAF layer at the edge
- Improve global performance with caching
- Protect against DDoS attacks with AWS Shield
Ideal architecture for production:
For production workloads, the recommended architecture would place the EC2 instance in a private subnet with no public IP address and use VPC Interface Endpoints for Systems Manager connectivity. Here's what that would look like:
Enhanced Network Architecture:
Key differences:
- EC2 in private subnet - No public IP, no direct internet access
- VPC Interface Endpoints - Enable SSM access without internet gateway (~$21.60/month for 3 endpoints)
- NAT Gateway - If EC2 needs outbound internet access (~$32.40/month + data transfer)
- CloudFront - Global distribution and edge protection
Why not include this in the current architecture?
VPC Interface Endpoints cost approximately $0.01/hour per endpoint (~$7.20/month each). For Systems Manager to work in a private subnet, you need 3 endpoints:
-
com.amazonaws.region.ssm com.amazonaws.region.ec2messagescom.amazonaws.region.ssmmessages
Total cost: ~$21.60/month just for the VPC Endpoints, plus additional complexity in setup and troubleshooting. For an educational article focused on WAF and application security fundamentals, this additional cost would make the architecture less accessible for learning purposes.
The current implementation keeps the EC2 in a public subnet but eliminates SSH exposure entirely through Systems Manager Session Manager. While the instance has a public IP, the Security Group ensures no direct access is possible, only the ALB can reach it on port 80. This achieves approximately 90% of the security benefit of a private subnet at 0% of the additional cost, making it ideal for learning, development, and testing environments.
Why this matters for your security journey:
This architecture demonstrates Layer 3, 4, and 7 protection fundamentals with encrypted transport, IAM based access control, and zero SSH exposure. It's an excellent educational starting point and works well for:
- Development and testing environments
- Learning AWS security concepts and best practices
- Understanding the defense in depth model
- Proof of concepts and demos
- Small internal tools with limited exposure
- Personal projects and portfolios
However, for production workloads handling real user data, PII, or serving public traffic at scale, you need the additional layers mentioned above. Security is not a single solution, it's a layered approach where each control addresses specific attack vectors and threat models.
Recommended next steps:
- Enable Session Manager logging to CloudWatch for audit trails (minimal cost)
- Add rate-based rules to WAF for basic request rate limiting
- Enable CloudWatch logging for WAF and create alarms for blocked requests
- Implement GuardDuty for threat detection ($4-$10/month for light usage)
- Use Security Hub for centralized security findings (additional cost)
- Evaluate private subnet migration with VPC Endpoints when budget allows
- Consider CloudFront for global distribution and additional edge protection
- Enable automated patching with Systems Manager Patch Manager
Each of these topics deserves its own deep dive, which I plan to cover in future articles as part of a comprehensive AWS security series.
AWS WAF pricing is based on:
- Number of Web ACLs
- Number of rules
- Number of requests inspected
A few things to know for this setup:
- AWS WAF is not included in the AWS Free Tier
- For low traffic environments, costs are typically minimal
- Costs stay predictable as long as you clean up resources after testing
If you're just experimenting, expect single digit dollar amounts per month for WAF itself, assuming moderate traffic.
Conclusion
Security Groups and AWS WAF aren't competing solutions. They work at different layers and solve different problems.
Security Groups control who can connect.
AWS WAF controls what those connections are trying to do.
Understanding which control applies at which layer is what makes it possible to build secure, scalable architectures, even for relatively simple workloads.
Protecting a web application doesn't require complex tooling. It requires putting the right controls at the right layers.
If you're running web applications on EC2 without WAF, this architecture is a solid starting point.
A note on this content
This article documents my learning journey with AWS security architecture. I'm sharing what I've learned and built, not claiming to be an expert. If you spot something that could be improved, or if you have a better approach, I genuinely want to hear about it.
Security architecture is complex, and there are often multiple valid ways to solve the same problem. The setup I've described here prioritizes learning and understanding core concepts over production grade completeness. Real production environments would require additional layers like GuardDuty, Security Hub, comprehensive logging, and a more robust network isolation strategy.
If you have feedback, corrections, or suggestions, please reach out that's how we all get better.
Thanks for reading.🙂

Top comments (0)