How to Use AWS WAF with CloudFront for Web Security
To use
AWS WAF with CloudFront, create a WAF web ACL with your security rules and then associate it with your CloudFront distribution. This setup filters incoming web traffic at the edge, protecting your site from attacks like SQL injection and cross-site scripting.Syntax
Using AWS WAF with CloudFront involves these main parts:
- Web ACL: A set of rules that define what traffic to allow, block, or count.
- Rules: Conditions like IP match, string match, or managed rule groups.
- Association: Linking the Web ACL to a CloudFront distribution to apply the rules.
terraform
resource "aws_wafv2_web_acl" "example" { name = "example-web-acl" scope = "CLOUDFRONT" description = "Protect CloudFront distribution" default_action { allow {} } rule { name = "BlockBadBots" priority = 1 statement { byte_match_statement { search_string = "BadBot" field_to_match { single_header { name = "user-agent" } } positional_constraint = "CONTAINS" text_transformations { priority = 0 type = "NONE" } } } action { block {} } visibility_config { sampled_requests_enabled = true cloudwatch_metrics_enabled = true metric_name = "BlockBadBots" } } visibility_config { cloudwatch_metrics_enabled = true metric_name = "exampleWebACL" sampled_requests_enabled = true } } resource "aws_cloudfront_distribution" "example" { origin { domain_name = "example.com" origin_id = "example-origin" } enabled = true is_ipv6_enabled = true default_root_object = "index.html" default_cache_behavior { target_origin_id = "example-origin" viewer_protocol_policy = "redirect-to-https" allowed_methods = ["GET", "HEAD"] forwarded_values { query_string = false cookies { forward = "none" } } } restrictions { geo_restriction { restriction_type = "none" } } viewer_certificate { cloudfront_default_certificate = true } web_acl_id = aws_wafv2_web_acl.example.arn }
Example
This example shows how to create a simple AWS WAF web ACL that blocks requests with a "BadBot" user-agent and attach it to a CloudFront distribution using Terraform.
terraform
provider "aws" { region = "us-east-1" } resource "aws_wafv2_web_acl" "example" { name = "example-web-acl" scope = "CLOUDFRONT" description = "Protect CloudFront distribution" default_action { allow {} } rule { name = "BlockBadBots" priority = 1 statement { byte_match_statement { search_string = "BadBot" field_to_match { single_header { name = "user-agent" } } positional_constraint = "CONTAINS" text_transformations { priority = 0 type = "NONE" } } } action { block {} } visibility_config { sampled_requests_enabled = true cloudwatch_metrics_enabled = true metric_name = "BlockBadBots" } } visibility_config { cloudwatch_metrics_enabled = true metric_name = "exampleWebACL" sampled_requests_enabled = true } } resource "aws_cloudfront_distribution" "example" { origin { domain_name = "example.com" origin_id = "example-origin" } enabled = true is_ipv6_enabled = true default_root_object = "index.html" default_cache_behavior { target_origin_id = "example-origin" viewer_protocol_policy = "redirect-to-https" allowed_methods = ["GET", "HEAD"] forwarded_values { query_string = false cookies { forward = "none" } } } restrictions { geo_restriction { restriction_type = "none" } } viewer_certificate { cloudfront_default_certificate = true } web_acl_id = aws_wafv2_web_acl.example.arn }
Output
Terraform will create a WAF web ACL named "example-web-acl" with a rule blocking requests containing "BadBot" in the User-Agent header, then create a CloudFront distribution that uses this web ACL to filter incoming traffic.
Common Pitfalls
Common mistakes when using WAF with CloudFront include:
- Not setting the
scopetoCLOUDFRONTin the WAF Web ACL, which is required for CloudFront. - Forgetting to associate the Web ACL ARN with the CloudFront distribution's
web_acl_id. - Using regional WAF Web ACLs instead of global scope for CloudFront.
- Not waiting for CloudFront distribution deployment after associating the Web ACL, causing delays in protection.
terraform
/* Wrong: scope set to REGIONAL (for ALB, not CloudFront) */ resource "aws_wafv2_web_acl" "wrong" { name = "wrong-scope" scope = "REGIONAL" # Incorrect for CloudFront default_action { allow {} } visibility_config { cloudwatch_metrics_enabled = true metric_name = "wrongScope" sampled_requests_enabled = true } } /* Right: scope set to CLOUDFRONT */ resource "aws_wafv2_web_acl" "right" { name = "right-scope" scope = "CLOUDFRONT" # Correct for CloudFront default_action { allow {} } visibility_config { cloudwatch_metrics_enabled = true metric_name = "rightScope" sampled_requests_enabled = true } }
Quick Reference
- WAF Web ACL scope: Must be
CLOUDFRONTfor CloudFront distributions. - Association: Use the
web_acl_idproperty in CloudFront distribution. - Rules: Use managed rule groups or custom rules to filter traffic.
- Deployment: Changes take time to propagate through CloudFront edge locations.
Key Takeaways
Always set the WAF Web ACL scope to CLOUDFRONT when protecting CloudFront distributions.
Attach the Web ACL to CloudFront using the web_acl_id property in the distribution configuration.
Use rules in the Web ACL to block or allow traffic based on headers, IPs, or managed rule groups.
Wait for CloudFront deployment to complete after associating the Web ACL for protection to be active.
Test your WAF rules carefully to avoid blocking legitimate traffic.