Protecting Web Apps with Cloud Armor WAF and Rate Limiting on External HTTP(S) Load Balancers

Most internet-facing applications need the same baseline protections: block common web attacks, slow down abusive clients, and reduce the impact of traffic spikes. Google Cloud Armor provides these controls at the edge, before traffic reaches your backends. It integrates directly with External Application Load Balancers and can enforce rules such as allow/deny, preconfigured WAF checks, and rate limiting.

This guide shows a practical starting configuration you can apply to a public web app or API hosted on GKE, Cloud Run (via serverless NEG), or a managed instance group behind an External HTTP(S) Load Balancer.

Request flow:

Client traffic
→ External HTTP(S) Load Balancer
→ Cloud Armor security policy
→ Backend service (GKE / Cloud Run / MIG)
→ Application

Cloud Armor evaluates rules on incoming requests and can block or throttle them without involving your application.

Prerequisites

  • A Google Cloud project with billing enabled

  • An External HTTP(S) Load Balancer already routing traffic to a backend service

  • gcloud installed and authenticated

  • Permission to manage Cloud Armor and Load Balancing resources (Compute Security Admin and Load Balancer Admin, or equivalent)

In the commands below, replace:

  • POLICY_NAME with your Cloud Armor policy name

  • BACKEND_SERVICE with the backend service name used by your load balancer

  • PROJECT_ID with your project

Set variables:
export PROJECT_ID=“your-project-id”
export POLICY_NAME=“edge-security-policy”
export BACKEND_SERVICE=“your-backend-service”
gcloud config set project “$PROJECT_ID”

Step 1: Create a Cloud Armor security policy

Create the policy with a default rule that allows traffic. You will add more specific rules above it.
gcloud compute security-policies create “$POLICY_NAME”
–description=“Baseline WAF and rate limiting for public traffic”

Cloud Armor rules are processed by priority: lower numbers are evaluated first.

Step 2: Add a baseline WAF rule (SQL injection and XSS)

A simple baseline is to block common SQL injection and XSS patterns using preconfigured WAF rules.

Example rule (deny with 403 if it matches):
gcloud compute security-policies rules create 1000
–security-policy “$POLICY_NAME”
–expression=“evaluatePreconfiguredWaf(‘sqli-v33-stable’, {‘sensitivity’: 2}) || evaluatePreconfiguredWaf(‘xss-v33-stable’, {‘sensitivity’: 2})”
–action=“deny-403”
–description=“Block common SQLi and XSS payloads”

Step 3: Add rate limiting for abuse protection

Rate limiting helps with brute-force attempts (login endpoints), scraping, and noisy clients. A good first pattern is: allow normal traffic, but return 429 when a client exceeds a threshold.

Example: throttle clients that exceed 60 requests per minute to /api/.
gcloud compute security-policies rules create 900
–security-policy “$POLICY_NAME”
–expression=“request.path.matches(‘^/api/’)”
–action=“throttle”
–rate-limit-threshold-count=60
–rate-limit-threshold-interval-sec=60
–conform-action=“allow”
–exceed-action=“deny-429”
–enforce-on-key=“IP”
–description=“Rate limit /api/ per client IP”

enforce-on-key=IP is a common default. If your clients sit behind NAT or proxies, you may need to consider how IP addresses appear at the load balancer.
If you want stronger protection, consider rate-based ban (temporary bans) instead of simple throttling.

Step 4: Attach the policy to your load balancer backend service

Cloud Armor policies are attached to backend services (not directly to the forwarding rule).
gcloud compute backend-services update “$BACKEND_SERVICE”
–global
–security-policy=“$POLICY_NAME”

If your backend service is regional, remove --global and use the correct regional flag.

Step 5: Validate behavior

  1. Validate WAF blocking (example SQLi-like input):

    curl -i “https://YOUR_DOMAIN_OR_IP/api/search?q=’ OR 1=1 --”
    Expected result: HTTP 403 if the request matches a WAF signature.

  2. Validate rate limiting by generating many requests quickly:
    for i in $(seq 1 200); do
    curl -s -o /dev/null -w “%{http_code}\n” “https://YOUR_DOMAIN_OR_IP/api/ping”
    done

    Expected result: some responses become HTTP 429 once the threshold is exceeded.

    Observability and troubleshooting

    • Enable and review load balancer logs in Cloud Logging. Cloud Armor decisions are reflected in request logs, which makes it easier to confirm which rule matched.

    • If you see unexpected blocks (false positives), lower WAF sensitivity, add narrow exceptions (for specific paths), or create allow rules for trusted sources above the WAF rule.

    • If rate limiting affects legitimate users, adjust thresholds or enforce on a different key depending on your client patterns.

This baseline policy is a good starting point, but it is not the final state for most production systems.

2 Likes