Sitemap
RepoBaby

RepoBaby

Pinky finger on the shift key. Head in the clouds.

Swapping Hardware Firewalls for AWS WAF: A Sysadmin’s Guide to Web Security

8 min readOct 6, 2026

--

I learned this the hard way, if I treat AWS WAF like a rack firewall with a web label on it, I create blind spots quickly. My first migration looked clean in a diagram, but traffic entered through CloudFront, an ALB, private APIs, and a partner link that I had not modeled. The appliance mindset gave me one perimeter. The cloud application gave me four.

What I want to make clear at the start is simple: AWS WAF is useful, but only if I map it to the actual ingress paths and keep it in the correct layer of the stack. This post is about that shift: where hardware habits break, where AWS WAF fits, and how I avoid the blind spots that appear during real migrations.

Why hardware habits break in AWS

On-premises, I used to anchor security around a choke point. Internet traffic hit the firewall, then the reverse proxy, then the application. In AWS, that pattern breaks because ingress is distributed, and ownership is split across the edge, the load balancer, the API layer, and VPC paths. Re-creating the old perimeter usually means that I duplicate rules and still miss paths.

AWS WAF is strong when I use it for what it is: Layer 7 filtering for HTTP and HTTPS threats aligned with the OWASP Top 10 guidance. It evaluates requests at services such as CloudFront, ALB, or API Gateway. It does not replace network controls for non-HTTP traffic, subnet boundaries, or egress policy. That distinction matters early.

I now separate concerns before I migrate anything:

  • Web filtering: request inspection, signatures, rate control, and bot patterns.
  • Network filtering: east-west traffic, non-HTTP protocols, and egress restrictions.

By network segmentation, I mean controlling which subnets, workloads, or services can communicate with each other at all. In AWS, that usually belongs in security groups, NACLs, routing boundaries, or tools such as AWS Network Firewall, not in WAF rules.

Watch out: If I port every legacy ACL into AWS unchanged, I usually preserve old mistakes and add new drift.

The practical shift is straightforward. I stop asking, “What box replaces my firewall?” I ask, “Which AWS layer owns this decision?” That framing makes the next architectural choice clearer.

Before I explain the control points, I want a side-by-side diagram that makes the old and new paths explicit.

Press enter or click to view image in full size
Comparison of legacy perimeter appliances and cloud-native, layered AWS security.

Where AWS WAF actually fits

I explain AWS WAF to sysadmins this way: it is a request-decision engine attached to specific ingress services, not a universal traffic firewall. I can associate it with Amazon CloudFront, ALB, API Gateway, AppSync, or Cognito. If traffic bypasses those attachment points, WAF never sees it, so design matters more than checkbox enablement.

A concrete example changed how I think about this. In one migration, the public site went through CloudFront with WAF attached, so everyone assumed that coverage was complete. But a partner integration still reached the origin through a separate ALB path with no matching inspection, and that path became the weak spot until we aligned the ingress design and attached the correct controls in both places.

The controls I actually use

My baseline usually starts with managed rule groups from AWS WAF, and then I add narrow custom rules. Managed groups catch common injection and protocol-abuse patterns. Custom rules are where I handle application-specific paths, request-size limits, or an allowlist for a health endpoint. Rate-based rules help when a path is noisy but not obviously malicious.

Rule capacity is a real constraint, not a documentation footnote. Every rule consumes web ACL capacity units, so a “just add one more rule” habit gets expensive in operations and design.

I also decide where TLS terminates because visibility changes at that point. If I terminate at CloudFront, I inspect closer to the edge and reduce origin load. If I terminate later at the ALB, I sometimes simplify origin troubleshooting but lose edge-enforcement opportunities.

Practical tip: I stage custom rules in count mode first. That gives me real request samples before I block production traffic.

That approach has saved me more than once. A narrow path rule that looked obvious on paper once flagged a legitimate mobile client because its header casing and payload shape differed from the browser traffic that we had tested. Count mode made that visible before the rule became an outage.

You will commonly run into policy sprawl once teams discover how easy rule creation looks in the console. That is why I prefer a hard comparison before I choose the rest of the stack.

Before I get into ownership, I want a concrete comparison table that pins each control to the correct tool.

Press enter or click to view image in full size

How layered design avoids blind spots

My opinionated view is that the best AWS design is layered, not pure. I put AWS WAF at ingress for HTTP protection, and then I keep AWS Network Firewall or an equivalent control inside the VPC for east-west and egress inspection. That reduces ambiguity. It also keeps me from forcing Layer 7 tooling to solve Layer 3 and Layer 4 problems.

The failure mode that I have seen most often is shared ownership with no boundaries. One team adds broad managed rules, another adds custom exceptions, and a third team mirrors restrictions in security groups or network firewall policies. Latency increases, debugging slows down, and nobody knows which rule blocked the request.

Get RepoBaby’s stories in your inbox

Join Medium for free to get updates from this writer.

I prefer a simple ownership model:

  • Platform team: baseline managed rules, associations, and logging pipelines.
  • Application team: narrow custom exceptions with expiration dates.
  • Security team: review, metrics, and incident tuning.

Note: I treat exceptions like temporary patches. If they do not expire, they become architecture.

This model helps me keep rule intent small and reviewable. It also makes change windows safer because I can tell whether a request was dropped at the edge, at the ALB path, or in the VPC. Once ownership is clear, I can show the flow and the logging paths that support it.

Before I cover evasions, I want a traffic-flow diagram that makes control ownership visible.

Press enter or click to view image in full size
AWS Shared Responsibility Model applied to a multi-layered web security architecture.

The bypasses that surprise teams

This is the part that I do not skip anymore. A green dashboard can still hide weak inspection if the parser and the payload format do not line up. I have seen cases in which a rule set looked solid in review, then missed requests because the application accepted formats that the inspection path handled less cleanly.

A few bypass patterns appear repeatedly:

  • Multipart payloads: boundary parsing changes what the engine can inspect cleanly.
  • Serialized blobs: attacks hide inside nested or encoded parameters.
  • Header oddities: unusual content types trigger inconsistent matching behavior.

One case that stays with me involved file uploads on an admin endpoint. The team had tested standard form posts and JSON APIs, but not multipart requests with mixed fields and binaries. The managed rules performed well on the obvious paths, yet the upload flow needed extra validation and tighter application-side controls because the request structure reduced how useful generic inspection was in that case.

Watch out: False negatives are not the only problem. Overbroad signatures can break file uploads, admin tools, and API clients.

I test WAF changes the same way I test application changes. I run expected bad cases, expected good cases, and edge encodings against the paths that I care about most. If I trust only managed rules, I am outsourcing my understanding of my own traffic.

To make rollout safer, I use a small configuration pattern that I can stage before enforcement.

{
"Rules": [
{
"Name": "AWSManagedRulesCommonRuleSet",
"Priority": 1,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesCommonRuleSet"
}
},
"Action": {
"Count": {} // Count mode for testing before enforcing
}
},
{
"Name": "RateBasedRule",
"Priority": 2,
"Statement": {
"RateBasedStatement": {
"Limit": 2000,
"AggregateKeyType": "IP"
}
},
"Action": {
"Block": {} // Block requests exceeding rate limit
}
},
{
"Name": "CustomBlockSpecificPath",
"Priority": 3,
"Statement": {
"ByteMatchStatement": {
"FieldToMatch": {
"UriPath": {}
},
"PositionalConstraint": "EXACTLY",
"SearchString": "/blocked-path"
}
},
"Action": {
"Block": {} // Custom blocking rule for a specific path
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "CustomBlockSpecificPath"
}
}
],
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "WebACL"
},
"LoggingConfiguration": {
"LogDestinationConfigs": [
"arn:aws:logs:region:account-id:log-group:log-group-name"
// Log WAF requests to CloudWatch
]
}
}

How I would migrate and measure it

My migration plan is boring by design, and that is why it works. First, I inventory current firewall intent instead of current firewall syntax. Then I split controls into web threats, network segmentation, and egress restrictions. Only after that do I attach WAF to the real ingress points and start in count mode.

Rollout sequence I trust

I follow this sequence:

  • Baseline current requests, paths, headers, and known exceptions.
  • Enable managed rules in count mode and compare logs for a week.
  • Add narrow custom rules, then promote only the ones with a clean signal.

I measure success with engineering metrics, not vendor screenshots. I watch added request latency, block quality, false-positive rate, and operational metrics such as MTTD and MTTR. If rule-evaluation depth grows while useful blocks stay flat, I know that I am adding complexity without value. In practice, most teams handle this best by keeping the baseline small and reviewing every exception monthly.

Practical tip: Duplicated inspection paths quietly raise spend. Request volume, extra rule groups, and overlapping VPC inspection add cost faster than most first migrations expect.

I also keep dashboards simple. CloudWatch, S3 logs, and a SIEM are enough if the questions are clear: what got blocked, where, and why. When a team is still building AWS fundamentals around these decisions, I usually point them first to the AWS documentation and then to focused reference material on related topics such as AWS IAM policies, VPC design patterns, and CloudFront fundamentals. That tends to be more useful than reading WAF documentation in isolation.

What I would keep in mind at the end

I would not try to swap a hardware firewall for AWS WAF one for one. I would map intent to layers, keep WAF at web ingress, and use network controls for everything that WAF cannot see. I would start small, measure latency and false positives, and treat exceptions as debt. If I stay disciplined about ownership and scope, AWS-native web security becomes easier to operate than the old perimeter ever was.

--

--

RepoBaby
RepoBaby

Pinky finger on the shift key. Head in the clouds.