Protect Django on AWS With AWS WAF
Put AWS WAF in front of a Django app on an ALB, CloudFront, or API Gateway: start with AWS managed rule groups in count mode, add rate-based rules for login and API endpoints, exclude rules that break uploads or admin forms, and log and tune blocked requests.
Problem statement
Your Django app on AWS faces scanners, credential stuffing against the login view, and request floods on expensive endpoints. AWS WAF can filter requests before they reach the ALB or CloudFront, using AWS managed rules for common exploits plus your own rate limits. The risk is false positives: managed SQL injection and size rules can block legitimate Django admin edits, rich text, and file uploads unless you roll out carefully.
- Create a web ACL and associate it with the ALB or CloudFront distribution in front of Django.
- Add AWS managed rule groups: Core rule set, Known bad inputs, IP reputation, and SQL database; start them in count mode.
- Add rate-based rules for
/accounts/login/,/admin/login/, and API paths. - Enable WAF logging, review counted matches for a week, and add scope-down statements or rule exclusions for legitimate traffic.
- Switch rule groups to block one at a time and monitor 403 rates.
Step-by-step solution
1. Create and attach the web ACL
Web ACLs for CloudFront live in us-east-1; for an ALB, create the ACL in the ALB's region. Attach it in the console or with Terraform (aws_wafv2_web_acl_association). Set the default action to Allow, and let rules block.
2. Add managed rule groups in count mode
Recommended starting set:
AWSManagedRulesCommonRuleSet: generic web exploits (XSS, path traversal, oversized bodies).AWSManagedRulesKnownBadInputsRuleSet: Log4j-style and other known bad patterns.AWSManagedRulesAmazonIpReputationList: known malicious sources.AWSManagedRulesSQLiRuleSet: SQL injection patterns.
Override each group's action to Count at first. Counted requests are logged but allowed.
3. Add rate-based rules
Protect the login views and expensive endpoints:
{
"Name": "login-rate-limit",
"Priority": 10,
"Action": { "Block": {} },
"Statement": {
"RateBasedStatement": {
"Limit": 100,
"EvaluationWindowSec": 300,
"AggregateKeyType": "IP",
"ScopeDownStatement": {
"ByteMatchStatement": {
"FieldToMatch": { "UriPath": {} },
"PositionalConstraint": "STARTS_WITH",
"SearchString": "/accounts/login/",
"TextTransformations": [{ "Priority": 0, "Type": "NONE" }]
}
}
}
},
"VisibilityConfig": { "SampledRequestsEnabled": true, "CloudWatchMetricsEnabled": true, "MetricName": "login-rate" }
}
Behind CloudFront, aggregate by the client IP that CloudFront forwards (forwarded IP configuration) only if the ACL is on the ALB; ACLs on CloudFront already see the real client.
4. Log and tune
Send WAF logs to CloudWatch Logs, S3, or Firehose (log group names must start with aws-waf-logs-). Query counted requests by rule and URI. Typical Django adjustments:
- Exclude
SizeRestrictions_BODYfrom the core rule set for upload endpoints, using a scope-down statement on the path. - Exclude SQLi body rules for
/admin/paths if staff edit content containing SQL-like text, and protect/admin/with other controls instead. - Allow your monitoring and office IPs with an IP set rule at a high priority.
5. Switch to block
Move one rule group at a time from count to block, watch 403 metrics and user reports for a few days, then continue. Return a custom response body for blocked requests so users know what happened.
Explanation
AWS WAF evaluates each request against the web ACL's rules in priority order. Managed rule groups are maintained by AWS and inspect headers, query strings, and request bodies for attack patterns. Because Django apps legitimately accept free text and files, body inspection is where false positives come from. Rate-based rules count requests per key over a window and are a reliable defense against brute force on login views, independent of payloads.
Edge cases / notes
- Body inspection limits. WAF inspects only the first part of large bodies (the limit depends on the resource type and configuration); it is not a substitute for Django's validation.
- Costs. WAF charges per web ACL, per rule, and per million requests; managed groups from marketplace vendors cost extra.
- Bot Control. The Bot Control managed group adds bot detection for a fee; useful for scraping and signup abuse.
Next steps
- Compare edge options in Protect Django From Bots With a WAF.
- Configure the load balancer with Deploy Django Behind an AWS ALB.
- Add server-side limits with Rate Limit Django With Nginx.
FAQ
Will AWS WAF break the Django admin?
Managed SQLi and core rules can block some admin edits. Start in count mode and add exclusions for affected paths.
Where should the web ACL go, CloudFront or ALB?
On CloudFront if you use it, so attacks are filtered at the edge; otherwise on the ALB.
Does WAF replace Django's own validation?
No. It filters common attacks, but Django's validation and CSRF protection remain essential.
How do I stop login brute force with WAF?
Add a rate-based rule scoped to the login paths, aggregated by client IP.