Back to Blog
DDoSGCPCloudflareCloud Security

Google Cloud Armor vs Cloudflare DDoS Protection: Path, Rules, and Defaults

BlackNeuron Research Team
September 24, 2026
14 min read
Google Cloud Armor vs Cloudflare DDoS Protection: Path, Rules, and Defaults

When you are evaluating Google Cloud Armor against Cloudflare for DDoS protection, the first thing to settle is that they are not the same kind of object.

Cloudflare is a network. You route a hostname into it through DNS and it sits in front of an origin that can live anywhere.

Cloud Armor is a policy. It exists only as a set of rules attached to a Google Cloud load balancer, and it protects whatever that load balancer routes to.

Almost every practical difference between them follows from that one fact: where each can stand in the request path, what its rules can see, which way its defaults lean, and what it leaves on your side of the line. That is what this post compares. It is not a ranking, and it is not a test procedure. Testing a Google Cloud deployment is covered in GCP DDoS testing, testing a Cloudflare zone in Cloudflare DDoS testing, and the method behind both in the complete guide to DDoS testing.

At a glance: Cloud Armor vs Cloudflare

DimensionGoogle Cloud ArmorCloudflare
What it isSecurity policies attached to a Google Cloud load balancerAn anycast reverse-proxy network in front of your origin
How traffic reaches itPoint DNS at a Google load balancer's frontend IPProxy a DNS record through Cloudflare
Unit of coverageThe backend service (or backend bucket) a policy is attached toThe proxied hostname
Where the origin can liveGoogle Cloud natively; elsewhere via internet or hybrid NEGs behind a Google load balancerAnywhere with a reachable listener, or behind an outbound Tunnel
Rule languageCommon Expression Language (CEL), one priority-ordered listCloudflare Rules language, split across phases that run in a fixed order
Adaptive L7 layerAdaptive Protection: detects and suggests a rule; auto-deploy is opt-inHTTP DDoS managed rules: enforce by default; you tune sensitivity down
Challenge actionRedirect to reCAPTCHA Enterprise, or score reCAPTCHA tokensManaged, JS or interactive challenge as a native rule action
Pricing shapeStandard is usage-based; Enterprise is a subscription tierPlan-tiered per zone; DDoS mitigation unmetered

Neither column wins. Each row is a design decision the vendor made, and most of them decide something you have to configure, not something the vendor does for you.

Where each one sits in the request path

Cloud Armor vs Cloudflare on the same request path One is a network in front of any origin. The other is a policy wherever a Google load balancer stands. CLOUDFLARE: a network you route into Client DNS record proxied Cloudflare anycast edge L3/L4 absorbed L7 DDoS rules WAF and rate limit Cache Origin: anywhere GCP, colo, another cloud Security phases run ahead of the cache: every request, hit or miss, is evaluated. Unit of coverage: the proxied hostname. Typical hole: a DNS-only record that skips the edge. CLOUD ARMOR: a policy on a Google load balancer Client DNS record LB frontend IP Google Front End + external Application LB L3/L4 absorbed Edge sec. policy Cloud CDN URL map backend service A backend policy: misses only backend service B (/api) no policy attached Unit of coverage: the backend service. Typical hole: a URL-map path to a service with no policy. Backends can be private (no external IP). Off-Google origins sit behind the same LB as internet or hybrid NEGs. BlackNeuron™
Google Cloud Armor and Cloudflare placed on the same request path: Cloudflare as an anycast edge joined by a proxied DNS record, with its security phases running ahead of its cache and an origin anywhere; Cloud Armor as policies on a Google external Application Load Balancer, an edge policy ahead of Cloud CDN and a backend policy that only sees cache misses, attached per backend service.

Cloudflare stands in front of everything you proxy

A Cloudflare zone is joined per DNS record. Proxy the record and the hostname resolves to Cloudflare's anycast addresses; the TCP and TLS handshakes terminate at the edge, and a separate connection goes to your origin.

The origin does not have to be anywhere in particular. A VM on Google Cloud, a rack in a colo and a SaaS backend can all sit behind the same zone.

Cloud Armor stands where a Google load balancer stands

Cloud Armor has no path of its own. Its policies are enforced at the Google Front End on behalf of a load balancer, so the question "is Cloud Armor in front of this?" reduces to "is a Google load balancer in front of this, and does the right backend service carry a policy?"

Off-Google origins can still be protected, but only by putting a Google external Application Load Balancer in front of them with an internet or hybrid network endpoint group as the backend. The load balancer is the entry ticket. There is no Cloud Armor without one.

The unit you attach coverage to differs

On Cloudflare the unit is the hostname. On Cloud Armor it is the backend service, and one hostname can fan out to several of them through the URL map.

That is where a Cloud Armor hole tends to appear. A path rule sends /api/* to a backend service added last quarter, the policy was never attached to it, and one URL under a protected hostname runs with only the platform baseline. The per-service attachment and how it drifts are drawn out in the GCP walkthrough; what matters for this comparison is that the Cloudflare hole has a different shape, a grey-cloud DNS record, and no URL map can create it.

Cache position is not the same

Cloudflare runs its security phases ahead of its cache. A custom rule or a rate-limiting rule sees every request, hit or miss.

Cloud Armor splits the job in two. An edge security policy is evaluated before Cloud CDN and can filter requests that would otherwise be served from cache. A backend security policy is evaluated only for requests that go to the backend, which with Cloud CDN enabled means cache misses.

The practical consequence is about counting. Edge security policies match on simple attributes such as source address and geography, and they cannot throttle or ban by rate. Rate limiting lives only in backend policies, so with Cloud CDN on, a Cloud Armor throttle counts misses, not requests.

Against a cache-busting flood that is exactly the traffic you want counted. A cap on a client's total request rate, hits included, is not something Cloud Armor can express, while Cloudflare's rate-limiting phase sees every request before the cache.

The rule model and what it can express

Rule evaluation: fixed phases vs one priority list How far an allow rule reaches is set by the model, not by what the rule's author meant. Cloudflare: phases in a fixed order 1. L7 DDoS managed rules runs first; tune sensitivity or action, cannot be skipped 2. Custom rules your order within the phase; Skip names later phases 3. Rate-limiting rules counted on the characteristics you choose 4. Managed WAF rules vendor and OWASP-derived rulesets An exemption skips only the phases it names. No customer rule outranks the DDoS phase. Cloud Armor: one list, lowest number first 100 allow: monitoring vendor CIDR 1000 throttle: per-client rate 1500 rate-based ban 2000 preconfigured WAF signatures 3000 Adaptive Protection auto-deploy max default rule A request from the allowed CIDR stops at 100. The throttle, ban, WAF and auto-deployed rules below it never run for that range. BlackNeuron™
Rule evaluation compared: Cloudflare runs its L7 DDoS managed rules in their own phase before any customer rule, then custom rules, rate-limiting rules and managed WAF rules in a fixed phase order, with a Skip action to exempt traffic from later phases; Cloud Armor evaluates one flat list by priority number, lowest first, where an allow rule at a high priority exempts traffic from every rule below it, including throttles, bans and an auto-deployed Adaptive Protection rule.

Two expression languages over similar fields

Both products let you match on the same raw material: source address and ASN, geography, method, path, headers, cookies, and TLS fingerprints.

Cloud Armor writes these in CEL, for example request.path.startsWith('/login') && origin.region_code == 'XX', with preconfigured WAF signatures called as functions such as evaluatePreconfiguredWaf('sqli-v33-stable'). Cloudflare's Rules language reads more like a Wireshark display filter: http.request.uri.path contains "/login" and ip.src.country eq "XX".

The expressiveness gap is smaller than the syntax gap. Where they part is in the fields each one owns: Cloudflare exposes its own bot score and threat signals to rules on the plans that include them, and Cloud Armor exposes reCAPTCHA Enterprise token scores. Each can only match on what its own network measures.

One list against a pipeline of phases

A Cloud Armor policy is a single list evaluated by priority number, lowest first, and the first match wins. Deny rules, throttles, rate-based bans and preconfigured WAF rules all sit in the same list, interleaved however you numbered them. The evaluation-order consequences inside Cloud Armor are their own subject.

Cloudflare fixes the order for you. Its L7 DDoS managed rules run in their own phase first, then your custom rules, then rate-limiting rules, then the managed WAF rules. You order rules within a phase, never across phases.

Where an allow rule reaches

This is the difference with the sharpest operational edge.

In Cloud Armor, an allow rule for your office range or a partner's egress at priority 100 outranks the throttle at 1000, the rate-based ban at 1500, and an auto-deployed Adaptive Protection rule wherever you numbered it. One broad allow, written for a monitoring vendor, can quietly exempt a whole CIDR from every L7 control in the policy.

In Cloudflare, an exemption is an explicit Skip action that names which later phases or products to skip, and no customer rule can skip the L7 DDoS phase, because it has already run. The exemption is narrower by construction, and the price is that you cannot write your way around a DDoS managed rule that misfires. You can only lower its sensitivity or change its action.

Neither design is safer in general. Cloud Armor gives you one ordering to reason about and lets you get it wrong everywhere at once; Cloudflare's ordering is harder to break and harder to bend when you need to.

A third answer between allow and deny

Against an L7 flood made of valid-looking requests, the useful action is often neither allow nor deny but "prove you are a browser."

Cloudflare makes that a first-class rule action: Managed Challenge, a JavaScript challenge, or an interactive one, chosen per rule. Cloud Armor reaches the same place through reCAPTCHA Enterprise, either by redirecting a matched request to a challenge page or by scoring a reCAPTCHA token the application already collects. The second path is expressive but requires the reCAPTCHA integration to be built before the flood arrives, so it belongs in the design, not in the incident.

Adaptive protection against managed rules: defaults that fail in opposite directions

The adaptive L7 layer: opposite defaults Same risk, parked in different places. What you have to prove depends on which way the default leans. Cloud Armor Adaptive Protection: default is to suggest Anomaly on a backend service Alert: signature, confidence, suggested rule Deployed by a human, or auto-deploy (opt-in)? Enforcing Cloud Armor rule Default failure: the attack is detected and alerted, and keeps reaching the backend until the rule lands. Cloudflare HTTP DDoS managed rules: default is to enforce Anomaly on a proxied zone Managed rule matches Mitigated at the edge by its default action You lower sensitivity or change the action Default failure: your own legitimate surge looks like an attack and is challenged or dropped until tuned. BlackNeuron™
Opposite defaults for the adaptive L7 layer: Cloud Armor Adaptive Protection detects an anomaly and raises an alert with a suggested rule, which enforces only after someone deploys it or auto-deploy is enabled, so its default failure is detection without enforcement; Cloudflare's HTTP DDoS managed rules detect and enforce by default, so its default failure is a legitimate surge being mitigated until someone lowers the sensitivity.

Both vendors put a traffic-learning layer above the static rules. The layers are built with opposite defaults.

Cloud Armor leans toward suggesting

Adaptive Protection learns a baseline per backend service and, on an anomaly, raises an alert carrying a signature, a confidence score and a suggested rule. By default the suggestion is text in an alert until someone deploys it, a gap measured in the GCP walkthrough and probed adversarially here.

The bridge is auto-deploy, an Enterprise-tier option that promotes suggestions to enforcing rules on its own once confidence and impact cross thresholds you set. It is opt-in, it needs a placeholder rule in the policy to land in, and its thresholds are yours to pick. A deployment can truthfully say it "has Adaptive Protection" and still have only the detector, with no bridge behind it.

Cloudflare leans toward enforcing

Cloudflare's HTTP DDoS managed ruleset is on for every proxied zone and enforces from the start. Each rule carries a default action and sensitivity, and your controls run the other way: lower a rule's sensitivity or change its action when it catches traffic you wanted.

Its adaptive rules, which learn an origin's own traffic profile, follow the same posture on the plans that include them.

What each default costs you

The two defaults move the same risk to different places.

Cloud Armor's default fails as an attack that is detected, alerted and not stopped. Cloudflare's default fails as a legitimate surge, a launch, a ticket drop, a marketing push, that looks like an attack and gets challenged or dropped until someone tunes the rule.

So the thing you must prove differs. On Cloud Armor, prove that detection turns into enforcement fast enough, by a human or by auto-deploy. On Cloudflare, prove that your own biggest legitimate peak passes through the managed rules untouched. Both are detect-versus-enforce questions pointed in opposite directions.

Layer 3 and 4: what the proxy absorbs and what it does not

Both products absorb connection-layer floods structurally when they terminate the connection. A SYN flood or a UDP flood against a Cloudflare-proxied hostname ends at Cloudflare's edge; the same flood against a Google external Application Load Balancer ends at the Google Front End. Neither ever forwards a half-open handshake to your backend.

The asymmetry is at the edges of the proxy model.

On Google Cloud, a passthrough Network Load Balancer or a VM with its own public IP is not behind a proxy at all. Packets arrive at the instance. Protection there comes from Advanced network DDoS protection and network edge security policies, both in the Enterprise tier, and a Cloud Armor policy written for the Application Load Balancer does nothing for them.

On Cloudflare, anything that is not a proxied HTTP hostname needs a different product: Spectrum for individual TCP or UDP applications, Magic Transit for whole prefixes. That packaging was set against Akamai's in the previous comparison and holds unchanged here.

The comparison-level point: on both, "we have DDoS protection" is true for the proxied HTTP path and has to be re-asked, product by product, for everything else.

Running both: Cloudflare in front of Cloud Armor

A common production shape is Cloudflare in front of a Google external Application Load Balancer with a Cloud Armor policy on it. The two layers work, but they change each other's inputs in ways neither product warns you about.

Cloudflare in front of Cloud Armor The second layer sees the first layer's addresses. Keys and trust have to be rebuilt around that. 10,000 users 0.5 rps each 300 attacking clients 20 rps each Cloudflare edge forwards from ~40 egress addresses Google external ALB + Cloud Armor policy connecting IP = Cloudflare sees ~40 sources, not 10,300 Backends private, no external IP Direct-to-LB request forged CF-Connecting-IP must be refused before the header is trusted Keyed on connecting IP 125 rps per key on a normal day, 275 under attack: the throttle lands on the edge itself Keyed on user IP header user 0.5 rps, attacker 20 rps: a 10 rps per-client limit separates them again Lock first, then trust Cloudflare ranges, plus a secret header or mTLS only your zone can present BlackNeuron™
Cloudflare in front of Cloud Armor: thousands of users and attacking clients arrive at Cloudflare's edge, which forwards them from a small set of Cloudflare egress addresses to the Google load balancer, so Cloud Armor sees the connecting IP of Cloudflare rather than the user; rate limits keyed on that address throttle the edge itself, keying on the user IP header fixes it, and that header can only be trusted once the load balancer refuses traffic that did not come through Cloudflare.

The connecting IP collapses

Behind Cloudflare, every request reaching the load balancer comes from a Cloudflare address. Tens of thousands of users arrive at Cloud Armor through a few dozen connecting IPs.

Any Cloud Armor rule keyed on source address now describes Cloudflare. A throttle keyed on IP turns into a throttle on the edge: on a normal day each edge address carries hundreds of users' worth of traffic and trips a per-client threshold, and during an attack the ban falls on the addresses carrying your legitimate users along with the attacker.

2026-09-24T06:07:35.796899 image/svg+xml Matplotlib v3.5.1, https://matplotlib.org/ 0 50 100 150 200 250 300 rps per key User, keyed on user IP Attacker, keyed on user IP Edge address, connecting IP, normal day Edge address, connecting IP, under attack 0.5 rps per key 20 rps per key 125 rps per key 275 rps per key 10 rps limit Model, not a measurement. Per-key rate seen by a Cloud Armor throttle behind Cloudflare, against a 10 rps per-client limit. Source: Model, not a measurement: this post’s arithmetic (10,000 users at 0.5 rps, 300 attackers at 20 rps, 40 edge egress addresses) Behind Cloudflare, a per-IP throttle counts the edge, not the user BlackNeuron™
Model, not a measurement: per-key request rate seen by a Cloud Armor throttle behind Cloudflare. Keyed on the end-user IP, a real user runs at 0.5 requests per second and an attacking client at 20, either side of a 10 per second threshold. Keyed on the connecting IP, each of 40 Cloudflare egress addresses carries 125 requests per second on a normal day and 275 under attack, all far above the threshold.

The arithmetic is simple. Ten thousand users at half a request per second is 5,000 rps; spread across forty edge addresses, that is 125 rps per key before a single attacker shows up. Add three hundred attacking clients at 20 rps each and every key carries 275. A 10 rps per-client limit cannot tell anyone apart.

Keying on the user, and why that needs the lock first

Cloud Armor can be told which request header carries the real client address, and rules and rate-limit keys can then use the user IP instead of the connecting one. Behind Cloudflare that header is CF-Connecting-IP. The same applies to geography: anything derived from the connecting address describes where Cloudflare forwarded from, not where the user is.

The header is only as honest as the path. If the load balancer's frontend also answers traffic that did not come through Cloudflare, an attacker sends requests straight to it with a different CF-Connecting-IP on each one, and the per-user limit becomes a per-request formality.

So the order matters. First make the load balancer refuse anything that did not come through Cloudflare, then trust the header.

Locking the load balancer to the edge

An allow rule for Cloudflare's published ranges with a default deny is the obvious lock, and it is incomplete for the same reason it is on any shared edge: those ranges are shared by every Cloudflare customer, so another account's zone pointed at your frontend IP passes the check.

Closing that takes something only your zone can present, a secret header added at your edge and required by a Cloud Armor rule, or mutual TLS at the load balancer against a client certificate issued to your zone alone. The origin-exposure post covers how the frontend address leaks in the first place.

Two learners, one filtered stream

Adaptive Protection behind Cloudflare learns a baseline of traffic Cloudflare has already filtered. That is not wrong, but it changes what an alert means: it fires on what got past the first layer, and its signatures are drawn from a stream in which source addresses carry almost no information.

When both layers are in place, test them as a stack. Each can pass its own check while the combination fails on keys, trust and ordering.

What each one leaves to you

Strip out the defaults and the list is similar on both, with different names on each item.

The path. On Cloudflare, every hostname you meant to protect has to be proxied. On Cloud Armor, every backend service behind every URL map has to carry a policy, and anything outside a Google Application Load Balancer needs a separate answer. The in-the-path check runs identically on both.

The origin. On Google Cloud the strongest lock is structural: backends with no external IP at all, reachable only from the load balancer. On Cloudflare the same state comes from a Tunnel with no inbound listener. Anything short of either, an allowlist of shared ranges, needs an authenticated layer on top.

Enforcement state. A Cloud Armor rule in preview mode and a Cloudflare rule set to Log both record matches and stop nothing.

Thresholds and keys. Rate-limit values and the key they count on are yours on both, and the key is the one that silently breaks when a second proxy is added.

The adaptive layer's direction. On Cloud Armor, turning suggestions into enforcement. On Cloudflare, keeping enforcement off your own peaks.

Cost exposure. Cloudflare meters DDoS mitigation on neither the attack's size nor its duration. Cloud Armor Standard is usage-based, so request volume evaluated by the policy is a billing axis, and the Enterprise tier adds DDoS bill protection. Model your own footprint against each rather than comparing list prices.

FAQ

What is the difference between Google Cloud Armor and Cloudflare?

Cloud Armor is a set of security policies enforced for Google Cloud load balancers; it protects what a Google load balancer routes to, attached per backend service. Cloudflare is an anycast reverse-proxy network you route hostnames into through DNS, in front of an origin that can live anywhere. Both filter L3/L4 floods at a proxy and apply WAF, rate-limit and adaptive L7 controls, but they differ in placement, rule model, and the default direction of their adaptive layers.

Is Cloud Armor or Cloudflare better for DDoS protection?

Neither is better in general. Both absorb large volumetric attacks at a proxy edge. Cloud Armor fits an estate already fronted by Google load balancers and keeps backends private inside a VPC; Cloudflare fits origins spread across providers and gives a single edge regardless of where they run. The outcome is decided more by configuration (path coverage, enforcement state, rate-limit keys, origin lock) than by the product chosen.

Does Cloud Armor Adaptive Protection block attacks automatically?

Only when auto-deploy is switched on. Out of the box it raises an alert with a suggested rule, and that rule enforces once someone deploys it. Auto-deploy, available in the Enterprise tier, can promote suggestions to enforcing rules automatically when confidence and impact cross thresholds you configure. Cloudflare's HTTP DDoS managed rules take the opposite default: they enforce from the start and you tune them down.

Can I use Cloudflare in front of Cloud Armor?

Yes, and it is a common pattern. Two adjustments are essential. Cloud Armor sees Cloudflare's addresses as the source, so rate limits and IP rules must key on the user IP carried in CF-Connecting-IP rather than the connecting IP. And that header can only be trusted once the load balancer refuses traffic that did not come through your Cloudflare zone, through an allowlist of Cloudflare ranges combined with a secret header or mutual TLS.

Does Cloud Armor protect VMs or Network Load Balancers?

A Cloud Armor policy for an Application Load Balancer does not. A passthrough Network Load Balancer or a VM with a public IP receives packets directly, and its protection comes from Advanced network DDoS protection and network edge security policies in the Enterprise tier, or from the always-on network protection every Google Cloud project has.

A policy and a network

The durable facts here will outlive every feature name in this post. Cloud Armor will remain a policy that goes wherever a Google load balancer goes and nowhere else. Cloudflare will remain a network that goes in front of whatever you point DNS at. One inherits Google's path and your URL map; the other inherits your DNS zone and whatever sits behind it.

Everything that changes, the default sensitivities, the auto-deploy thresholds, the header names, the tier boundaries, sits on top of that difference. The failures do too: a backend service with no policy, a hostname left unproxied, an allow rule numbered too low, a throttle counting Cloudflare instead of your users, a detector no one wired to enforce, a managed rule no one checked against launch day.

None of those are the vendor's to find. Pick the product whose shape matches where your traffic already goes, then spend the effort on the joins, because the joins are where both of them are only as good as the person who configured them.