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
| Dimension | Google Cloud Armor | Cloudflare |
|---|---|---|
| What it is | Security policies attached to a Google Cloud load balancer | An anycast reverse-proxy network in front of your origin |
| How traffic reaches it | Point DNS at a Google load balancer's frontend IP | Proxy a DNS record through Cloudflare |
| Unit of coverage | The backend service (or backend bucket) a policy is attached to | The proxied hostname |
| Where the origin can live | Google Cloud natively; elsewhere via internet or hybrid NEGs behind a Google load balancer | Anywhere with a reachable listener, or behind an outbound Tunnel |
| Rule language | Common Expression Language (CEL), one priority-ordered list | Cloudflare Rules language, split across phases that run in a fixed order |
| Adaptive L7 layer | Adaptive Protection: detects and suggests a rule; auto-deploy is opt-in | HTTP DDoS managed rules: enforce by default; you tune sensitivity down |
| Challenge action | Redirect to reCAPTCHA Enterprise, or score reCAPTCHA tokens | Managed, JS or interactive challenge as a native rule action |
| Pricing shape | Standard is usage-based; Enterprise is a subscription tier | Plan-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
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
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
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.
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.
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.
