Cloud Run services can be shielded from common web attacks by integrating with Cloud Armor, Google Cloud’s DDoS and WAF service.

Let’s see this in action. Imagine you have a simple Cloud Run service that echoes back whatever it receives.

gcloud run deploy echo-service \
  --image gcr.io/cloudrun/hello \
  --platform managed \
  --region us-central1 \
  --allow-unauthenticated

Now, let’s send a malicious request to it. Without Cloud Armor, this request would hit Cloud Run directly.

curl -X POST "https://echo-service-abcdef12345-uc.a.run.app" -d "<h1>XSS Attack</h1><script>alert('XSS')</script>"

The Cloud Run service would happily display the injected script. Now, let’s put Cloud Armor in front of it.

First, create a Cloud Armor security policy.

gcloud compute security-policies create cloudrun-waf-policy \
  --description "WAF policy for Cloud Run"

Next, add a rule to block common cross-site scripting (XSS) attempts.

gcloud compute security-policies rules create 1000 \
  --security-policy cloudrun-waf-policy \
  --expression "evaluatePreconfiguredExpr('xss-stable')" \
  --action "deny-403" \
  --description "Block common XSS attacks"

The evaluatePreconfiguredExpr('xss-stable') part is key here. Cloud Armor has a library of preconfigured rules that cover common attack vectors like XSS, SQL injection, and directory traversal. The deny-403 action means that if a request matches this rule, Cloud Armor will block it and return a 403 Forbidden error.

Now, attach this policy to your Cloud Run service. This is done via a load balancer. You’ll need to create a serverless network endpoint group (NEG) for your Cloud Run service.

gcloud compute network-endpoint-groups create echo-neg \
  --region=us-central1 \
  --network-endpoint-type=SERVERLESS \
  --cloud-run-service=echo-service

Then, create a global forwarding rule and a backend service that points to this NEG.

gcloud compute backend-services create cloudrun-backend \
  --global \
  --load-balancing-scheme=EXTERNAL_MANAGED \
  --protocol=HTTPS \
  --enable-cdn # Optional, but good practice
gcloud compute backend-services add-backend cloudrun-backend \
  --global \
  --network-endpoint-group=echo-neg \
  --network-endpoint-group-region=us-central1

Now, associate the security policy with the backend service.

gcloud compute backend-services update cloudrun-backend \
  --global \
  --security-policy=cloudrun-waf-policy

Finally, create a URL map and a global forwarding rule to direct traffic to your backend service.

gcloud compute url-maps create cloudrun-url-map \
  --default-service=cloudrun-backend
gcloud compute target-https-proxies create cloudrun-https-proxy \
  --url-map=cloudrun-url-map \
  --ssl-certificates=YOUR_SSL_CERTIFICATE_NAME # You'll need to create/have an SSL certificate

You’ll also need an SSL certificate managed by Google or uploaded. For simplicity, let’s assume you have one named my-ssl-cert.

gcloud compute target-https-proxies create cloudrun-https-proxy \
  --url-map=cloudrun-url-map \
  --ssl-certificates=my-ssl-cert

And the forwarding rule:

gcloud compute forwarding-rules create cloudrun-forwarding-rule \
  --global \
  --ports=443 \
  --target-https-proxy=cloudrun-https-proxy \
  --load-balancing-scheme=EXTERNAL_MANAGED \
  --address=YOUR_STATIC_IP_ADDRESS # Reserve a static IP address first

Make sure to reserve a static IP address and use it here.

Now, when you try to send that same malicious request again:

curl -X POST "https://YOUR_STATIC_IP_ADDRESS" -d "<h1>XSS Attack</h1><script>alert('XSS')</script>"

You’ll receive a 403 Forbidden error, and your Cloud Run service will be protected.

The real magic of Cloud Armor isn’t just blocking known bad patterns. It’s also about the granular control and the ability to create custom rules based on request headers, IP addresses, geographic locations, and even the presence or absence of specific cookies. For example, you could block all requests not originating from a specific country or allow access only from a list of trusted IP addresses.

What most people miss is how precisely you can define the scope of your WAF rules. You don’t have to apply a broad "block XSS" rule to your entire service. You can create a rule that only triggers if a specific header is present and the expression matches, or you can apply different rules to different parts of your application by using path-based routing with your load balancer. This allows for highly customized security postures without sacrificing flexibility.

Once your WAF rules are in place and you’re confident in their configuration, the next step is to explore rate limiting to prevent brute-force attacks and ensure service availability.

Want structured learning?

Take the full Cloud-run course →