A CDN is sold as DDoS protection. In practice, it only protects the traffic it can serve from its own cache.
Every response the edge cannot cache is a response your origin has to generate itself. So under a flood of those requests, the CDN forwards all of them, and your origin takes the full load as if the edge were not there.
The dashboard stays green the whole time. Caching almost nothing looks identical to caching everything, right up until the origin falls over.
This is edge cache absorption, and it is one of the quietest gaps in cloud resilience. It is also exactly the kind of assumption a thorough DDoS test is meant to check rather than trust.
The framing here is defensive throughout. These are the measurements you take against your own site, under authorization, so you know what your edge actually absorbs before an attacker finds out for you.
Why a CDN only absorbs what it caches
The mistake is treating a CDN as a wall. It is not a wall. It is a cache with a filter in front.
When a request arrives at the edge and the object is in cache, the edge answers it directly. Your origin never sees that request. That is absorption, and it is the entire mechanism by which a CDN blunts a flood.
When the object is not cacheable, the edge does the honest thing: it forwards the request to your origin and passes the answer back. Your origin did the work. Repeat that a few hundred thousand times a second and the edge has faithfully relayed a flood straight to your server.
So the protection a CDN gives you against volumetric load is not a property of the vendor. It is a property of your response headers, which decide what the edge is allowed to keep.
The arithmetic is unforgiving. The chart below is a simple simulation of one workload under a fixed flood, swept across cache hit-ratios.
At a 0 percent hit-ratio the origin sees the whole flood. At 95 percent it sees a twentieth of it. Installing a CDN does not move you along that curve. Caching does.
The broader picture of what a flood actually targets is in Understanding DDoS Attack Vectors.
What quietly makes your responses uncacheable
None of the reasons below require a misconfigured CDN. They are ordinary application defaults, and any one of them on the wrong path turns your expensive edge into a fast reverse proxy.
no-store and private
Frameworks apply these by default to anything that looks dynamic. Cache-Control: private or no-store is perfectly correct for a logged-in account page. It also tells every shared cache, including your CDN, not to keep a copy. Every hit then goes to origin.
max-age=0 and no-cache
These do not forbid caching outright, but they force the edge to revalidate with the origin on every request. The origin is back in the loop for each hit, which is the exact thing you were trying to avoid under load.
A Set-Cookie on the response
A response that sets a cookie is treated as user-specific and is not stored by shared caches. One analytics or CSRF cookie attached to your HTML, or worse to your static assets, can make a large part of your site uncacheable without anyone noticing.
The provider default
On Cloudflare, anything without an explicit cache rule returns cf-cache-status: DYNAMIC, which means the edge did not cache it. Most sites never add the rule, then assume the orange cloud is caching for them. It is not.
Genuinely dynamic pages, which is normal
Some responses simply cannot be cached, and that is fine. A personalized dashboard should be private. The point is not to cache everything. It is to know which responses hit your origin, and to make sure the ones that carry the flood are not among them.
Static assets are what a flood actually hits
A volumetric flood is overwhelmingly repeated requests for the same objects: the scripts, stylesheets, fonts, and images that every page load pulls. These are precisely the things that should live in the edge cache.
When they do, the edge absorbs the repetition and the origin barely notices. When they do not, every repeat is a fresh trip to your server.
The split is the whole story. Cacheable assets are absorbed at the edge. Uncacheable responses pass through. Your origin load under attack is set by which side of that line your busiest objects fall on.
This is the same reasoning behind hiding your origin so the flood cannot skip the edge entirely, covered in origin IP exposure. Absorption and exposure are two halves of one question: does the edge see the traffic, and does it keep it.
How to test your edge absorption
You cannot tell from a dashboard. You have to ask the edge directly, for your own URLs, and read what it says.
The mechanism is a two-step check per URL. Fetch it once to warm the edge, fetch it again, and read the cache headers on the second response.
# The second fetch is the one that tells the truth.
curl -sI https://example.com/assets/app.js | grep -iE 'cf-cache-status|x-cache|age|cache-control'
A cf-cache-status: HIT, an x-cache: Hit, or an Age greater than zero means the edge served it and your origin was untouched. A MISS on a warmed object, or a DYNAMIC, or a no-store, means that request reached your server.
Do that across your landing page, your static assets, and a few internal pages, and you have an honest absorption number. Repeating that by hand across a whole site is tedious, which is the usual reason it never gets done.
Or run it automatically
We packaged this exact check into a small, open-source Rust tool, edge-cache-check. It is free on GitHub: github.com/BlackNeuron-ai/edge-cache-check.
Point it at a domain you own. It fingerprints your edge, samples your landing page and its static assets, probes each URL twice, and reports what fraction the edge actually serves from cache, with the reason anything falls through.
cargo run --release -- example.com
It prints EDGE for anything served from cache, ORIGIN for anything that reaches your server, and a STRONG, PARTIAL, or WEAK verdict for the whole sample. No attack traffic, authorized self-testing only.
The full build story, and why a two-fetch design matters, is on dev.to: Your CDN says it is protecting you. I checked, and 0% of the traffic was cached. The code is on GitHub at github.com/BlackNeuron-ai/edge-cache-check.
Keeping the whole exercise inside a safe boundary is the subject of running a DDoS test without disrupting production.
Fixing weak absorption
Finding a low absorption number is only useful if the fix is confirmed by a re-test. The remediations, in order.
Make your static assets genuinely cacheable
Serve assets with a long max-age and immutable, and make sure nothing attaches a Set-Cookie to them. Fingerprinted asset filenames make aggressive caching safe, because a change ships a new URL rather than mutating an old one.
Add an explicit cache rule at the edge
Do not trust the provider default, which on Cloudflare is to cache nothing. Put a cache rule on the paths that can tolerate it, so the edge keeps what it is allowed to keep. The provider-specific contours are worked through in Cloudflare DDoS testing.
Defend the paths that cannot be cached
For genuinely dynamic routes, absorption is not the tool. Edge rate-limiting and a WAF are, so the origin is not the only thing standing between a flood and your application. Turning all of this into a single posture score is covered in DDoS resilience testing.
Absorption is one pillar of resilience, not the whole of it, but it is the pillar people assume they get for free the moment they put a CDN in front. Our Patent-Pending adaptive methodology exists precisely because these gaps hide until something changes its behavior in front of them.
FAQ
Does a CDN stop DDoS attacks?
Only for the traffic it caches. A CDN absorbs a flood by answering repeated requests from its edge cache. Any response it cannot cache is forwarded to your origin, so a flood of uncacheable requests reaches your server unfiltered.
What is a good CDN cache hit-ratio?
For static assets, aim high, well above 90 percent, because those objects carry the repetitive load a flood generates. Dynamic, personalized pages will and should have a low hit-ratio; the goal is a high ratio on the objects that dominate volume, not on everything.
Why is my CDN not caching my site?
The usual causes are a Cache-Control: private, no-store, or max-age=0 header, a Set-Cookie on the response, or a provider default such as Cloudflare DYNAMIC where no cache rule was ever added. Each forces the request through to the origin.
How do I measure what my edge actually absorbs?
Fetch each URL twice and read the cache headers (cf-cache-status, x-cache, Age) on the warmed second response. A hit means the edge served it; a miss or a no-store means your origin did. The open-source edge-cache-check tool automates this across a whole site.
The dashboard is not the test
Weak edge absorption survives in well-run environments because everything looks correct from the outside.
The site loads through the CDN. The provider dashboard is green. The edge is clearly doing something.
None of that reveals the one number that decides your fate under a flood: how much of your traffic the origin still has to serve itself.
The only way to know is to ask the edge, object by object, and count what it keeps.
