Web Application Firewall (WAF) Guide: What It Does and How to Deploy One
A WAF inspects HTTP traffic and blocks attacks like SQL injection, XSS, and CSRF. Learn how WAFs work, the difference between detection and prevention mode, and how to deploy one in front of your web app.
A Web Application Firewall (WAF) sits between your users and your web application, inspecting HTTP/HTTPS traffic for attack patterns. Unlike a network firewall (which operates at Layer 3/4 and inspects IP/port), a WAF operates at Layer 7 and understands HTTP — it can read request bodies, decode URL encoding, parse JSON, and identify SQL injection patterns in query strings.
A WAF is not a substitute for secure code, but it's a critical defense-in-depth layer that catches attacks, reduces noise, and buys time when zero-day vulnerabilities are disclosed.
What a WAF Can (and Can't) Do
Can do:
- Block common attacks: SQL injection, XSS, command injection, path traversal
- Rate limit requests (brute force, credential stuffing, scraping)
- Block malicious IPs and known bad user agents
- Enforce HTTP method restrictions
- Block suspicious request patterns (oversized headers, null bytes)
- Virtual patching: block exploitation of known vulnerabilities before you can patch
- DDoS mitigation (especially at higher layers)
- Bot management
Can't do:
- Fix insecure application code
- Prevent all attacks (attackers continuously develop WAF bypass techniques)
- Replace secure development practices or regular penetration testing
- Protect against insider threats or compromised credentials
- Fully prevent application logic flaws
A WAF is not a magic box. Think of it as a hardened security guard that stops the obvious attacks — a determined, skilled attacker may still find ways past it.
WAF Types
Cloud WAF (Most Common for Modern Apps)
Runs as a reverse proxy or DNS-based service in the cloud provider's infrastructure. Traffic is routed through the WAF before reaching your servers.
| Provider | Product |
|---|---|
| Cloudflare | Cloudflare WAF |
| AWS | AWS WAF (v2) |
| Google Cloud | Cloud Armor |
| Azure | Azure Application Gateway WAF |
| Fastly | Next-Gen WAF (Signal Sciences) |
| Imperva | Cloud WAF |
| Akamai | Kona Site Defender |
Pros: No infrastructure to manage, automatic scaling, integrated DDoS protection, globally distributed Cons: Traffic decrypted at the WAF (man-in-the-middle by design), latency (minimal with good providers), cost
Application-Embedded WAF
The WAF runs as middleware within your application.
// ModSecurity via NGINX
// nginx.conf
server {
ModSecurityEnabled on;
ModSecurityConfig modsecurity.conf;
}
// Node.js with express-waf (simplified example)
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
app.use(helmet());
app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));
Pros: No external traffic routing, lowest latency, most context-aware Cons: Resource overhead, more complex to maintain, doesn't protect non-HTTP traffic
Network WAF
Hardware appliance or VM placed in the network path, before traffic reaches your servers.
Pros: Protects all services in the network segment Cons: Expensive, complex to manage, becomes a single point of failure if not HA
OWASP Core Rule Set (CRS)
The OWASP ModSecurity Core Rule Set is the most widely deployed WAF rule set, available for ModSecurity, nginx, AWS WAF, Cloudflare, and others. It provides rules for all OWASP Top 10 categories.
CRS detection scores: each rule assigns a score to a request. When the accumulated score exceeds a threshold, the request is blocked. This reduces false positives from single-rule matching.
Configuring CRS anomaly scoring threshold
# Lower threshold = more aggressive, more false positives
# Higher threshold = less aggressive, more bypasses
# Default: 5 (inbound), 4 (outbound)
SecDefaultAction "phase:2,log,auditlog,pass"
SecPolicyEnableFailed "deny"
# Paranoia level: 1-4 (1=least strict, 4=strictest)
# Start with PL1, increase as you tune out false positives
Detection Mode vs Prevention Mode
Detection Mode (Logging Only)
WAF identifies potential attacks and logs them but does not block requests. All traffic passes through normally.
Use this when:
- Initially deploying a WAF
- After changing rules
- Troubleshooting false positives
# AWS WAF: Set web ACL action to COUNT instead of BLOCK
aws wafv2 update-web-acl \
--name my-waf \
--scope REGIONAL \
--id <id> \
--default-action Count={}
Prevention Mode (Blocking)
WAF blocks requests matching configured rules.
Best practice: Always start in detection mode, analyze logs for 1–2 weeks to identify false positives, then switch to prevention mode with tuned exceptions.
Deploying AWS WAF
# Create a Web ACL
aws wafv2 create-web-acl \
--name my-app-waf \
--scope REGIONAL \
--default-action Allow={} \
--rules '[
{
"Name": "AWSManagedRulesCommonRuleSet",
"Priority": 1,
"OverrideAction": {"None": {}},
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesCommonRuleSet"
}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "CommonRuleSetMetric"
}
},
{
"Name": "AWSManagedRulesSQLiRuleSet",
"Priority": 2,
"OverrideAction": {"None": {}},
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesSQLiRuleSet"
}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "SQLiRuleSetMetric"
}
}
]' \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=MyWAFMetric \
--region us-east-1
Attach to an ALB:
aws wafv2 associate-web-acl \
--web-acl-arn <web-acl-arn> \
--resource-arn <alb-arn>
Rate Limiting with WAF
Rate limiting is one of the most effective WAF capabilities, protecting against:
- Brute force authentication
- Credential stuffing
- API scraping
- DDoS at the application layer
Cloudflare Rate Limiting example
Rule: Block if requests from same IP exceed 20/minute to /api/auth/login
Action: Block for 10 minutes
AWS WAF Rate Limiting
{
"Name": "RateLimitRule",
"Priority": 0,
"Statement": {
"RateBasedStatement": {
"Limit": 300,
"AggregateKeyType": "IP",
"ScopeDownStatement": {
"ByteMatchStatement": {
"SearchString": "/api/auth",
"FieldToMatch": {"UriPath": {}},
"TextTransformations": [{"Priority": 0, "Type": "LOWERCASE"}],
"PositionalConstraint": "STARTS_WITH"
}
}
}
},
"Action": {"Block": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "RateLimitMetric"
}
}
Tuning: Handling False Positives
False positives (legitimate requests blocked) are the biggest WAF operational challenge.
Common sources of false positives:
- Rich text editors that submit HTML in form fields (triggers XSS rules)
- JSON APIs with deeply nested structures
- Programming content (code snippets in comments trigger injection rules)
- Legitimate SQL-like content in search queries
Tuning strategies:
- Count mode first: run in detection to understand your baseline before blocking
- Exception rules: exclude specific paths from specific rules (not from all rules)
- Paranoia levels: start at PL1, move to PL2 only after eliminating PL1 false positives
- Custom rules: explicitly allow known-good patterns before the detection rules run
# AWS WAF: Add an exception for a specific path
# Exclude /api/content-editor from XSS rules (rich text editor endpoint)
{
"ExcludedRules": [
{"Name": "CrossSiteScripting_BODY"}
]
}
A well-tuned WAF in prevention mode typically reduces attack surface significantly while adding minimal false positive rate for legitimate traffic (target: <0.1% of production traffic blocked).