Deploying Cloud Armor with HTTP(S) Load Balancing: Minimal Design, Trust Boundaries, and Operational Safeguards
Deploy a global HTTP(S) Load Balancer with Cloud Armor to shield a stateless web app. Learn the minimal design, trust boundaries, operational checks, failure modes, and when to redesign.
16 Apr 2026, 16:53 UTC

Problem Statement
You run a stateless web application that must be available worldwide, yet you need to protect it from DDoS, OWASP Top‑10 web attacks, and unwanted IP traffic. The simplest way in Google Cloud is to place a global external HTTP(S) Load Balancer in front of your backend instances and attach a Cloud Armor security policy. This architecture note explains the smallest viable design, how trust and data boundaries are enforced, what operational checks you should run, and when you might need to redesign.
Requirements & Minimal Design
- Global external HTTP(S) Load Balancer (URL map → target HTTP(S) proxy → backend service)
- Managed instance group (or Cloud Run/Compute Engine) as the backend
- Cloud Armor security policy attached to the target proxy
- Default deny rule, followed by allow rules for trusted IP ranges and rate‑based rules for public traffic
- Health checks that bypass rate limits
Step‑by‑Step Deployment
# 1. Create a security policy
# Replace with your project ID
# The policy will start with a broad allow rule (0.0.0.0/0) and a deny rule for a known bad IP
gcloud compute security-policies create webapp-policy \
--description "Protect stateless web app" \
--project <PROJECT>
# 2. Add a default allow rule (must be first)
# This rule is a placeholder; tighten it before moving to production
gcloud compute security-policies rules create 0 \
--security-policy webapp-policy \
--action allow \
--src-ip-ranges 0.0.0.0/0 \
--description "Broad allow – to be tightened" \
--project <PROJECT>
# 3. Add a deny rule for a malicious IP
gcloud compute security-policies rules create 1 \
--security-policy webapp-policy \
--action deny(403) \
--src-ip-ranges 203.0.113.45/32 \
--description "Block known bad IP" \
--project <PROJECT>
# 4. Add a rate‑based rule (example: 100 req/s per IP)
gcloud compute security-policies rules create 2 \
--security-policy webapp-policy \
--action deny(429) \
--src-ip-ranges 0.0.0.0/0 \
--rate-limit-interval-sec 1 \
--rate-limit-threshold 100 \
--description "Rate limit public traffic" \
--project <PROJECT>
# 5. Attach the policy to the target HTTP(S) proxy
# Replace with your proxy name
gcloud compute target-http-proxies update <TARGET_PROXY> \
--security-policy webapp-policy \
--project <PROJECT>
Health‑Check Exemption
Cloud Armor applies rules to every request that reaches the load balancer, including health‑check probes. If a rate‑based rule blocks the probe, the backend instances will be marked unhealthy. To avoid this, configure your health‑check path (e.g., /healthz) and add a rule that exempts that path from rate limits:
gcloud compute security-policies rules create 3 \
--security-policy webapp-policy \
--action allow \
--src-ip-ranges 0.0.0.0/0 \
--expression "request.path.matches("^/healthz$")" \
--description "Health‑check bypass" \
--project <PROJECT>
Trust & Data Boundaries
- Inspection Point: Cloud Armor evaluates traffic before it reaches your backend instances, ensuring malicious requests never hit your application.
- Policy Storage: Policies live in IAM; only users with
roles/compute.securityAdmincan modify them. - Encryption: Traffic is encrypted end‑to‑end via HTTPS. Cloud Armor does not decrypt payloads; if you need deep inspection, terminate TLS at the load balancer or use a WAF that supports it.
- IP Source: Cloud Armor uses the source IP from the TCP header, not the
X‑Forwarded‑Forheader. If your app relies on that header, validate it in the application layer.
Operational Checks
- Health‑Check Validation: Verify backend health checks return
200 OKafter policy changes. - Logging & Alerting:
- Enable Cloud Armor logs:
gcloud compute security-policies update webapp-policy --enable-logging - Create a Cloud Logging alert for high
denycounts (e.g., > 10 per minute).
- Enable Cloud Armor logs:
- Latency & Error Monitoring: After each policy change, watch
latencyand4xx/5xxmetrics in Cloud Monitoring. - Rollback Test: Use the Cloud Armor API to revert a rule and confirm traffic flow returns to expected behavior.
Failure Modes & Mitigations
| Failure Mode | Impact | Mitigation |
|---|---|---|
| Overly restrictive rules | Legitimate traffic denied, 403/429 responses | Start with a broad allow rule; tighten gradually; monitor deny counts. |
| Misconfigured rate limits | Service degradation or throttling of legitimate clients | Test with load‑testing tools; exempt health‑check paths; adjust thresholds. |
| Policy propagation delay | Temporary inconsistent behavior during rollout | Accept a few seconds delay; avoid critical traffic spikes during changes. |
| IP spoofing | Malicious traffic bypasses policy | Validate X‑Forwarded‑For in application; use Cloud Armor’s allow-src-ip-addresses with known proxies. |
When to Redesign
- Microservices: If you split the app into multiple services requiring distinct policies, attach a separate Cloud Armor policy to each target proxy.
- WebSocket/gRPC: These protocols need a regional load balancer; Cloud Armor supports HTTP(S) only, so consider a different ingress (e.g., Istio) or a WAF that handles these protocols.
- Compliance/Data Residency: Regional load balancers may be required; Cloud Armor can still be used but the policy must be attached per region.
- Custom WAF Logic: Rules that depend on request body content or complex regex patterns may exceed Cloud Armor’s rule language; in that case migrate to Cloudflare, a third‑party WAF, or a custom reverse proxy.
Practical Verification
After deploying, run:
# From a known malicious IP: expect 403
curl -I https://<LOAD_BALANCER_IP> -H "Host: example.com"
# From a legitimate IP: expect 200
curl -I https://<LOAD_BALANCER_IP> -H "Host: example.com"
# Simulate rate limit: send >100 req/s from a single IP and observe 429
ab -n 200 -c 200 https://<LOAD_BALANCER_IP>/
Check Cloud Logging for deny events and verify alert thresholds. If any legitimate traffic is blocked, refine the allow rules or adjust rate limits.
Conclusion
By following this minimal design – a single global load balancer, a Cloud Armor policy with a default deny, basic allow and rate‑based rules, and health‑check exemptions – you can protect a stateless web application from common web attacks while maintaining high availability. Continuous monitoring, logging, and incremental rule tightening are essential to avoid accidental denial of service. When your architecture evolves (microservices, WebSocket, compliance), revisit the design and consider attaching per‑service policies or migrating to a more capable WAF.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.