Cloudflare in front, 2026-10-05 to 2026-10-19 (15 days of production)

You put a reverse proxy in front. Here is what it let through.

22,293

requests the CDN passed and Guard Core blocked in 15 days

8,257

requests the CDN blocked on the same services

On the services that run Guard Core, the guard blocked 2.7x what the CDN blocked. 73% of all blocked requests on those services got past the CDN and were stopped in the app.

The objection

We keep hearing the same answer

When we propose application-layer protection, maintainers answer with the same advice. We take it seriously, so we ran it: a CDN and a hardened web server in front, for 15 days, with real traffic. These are their words, with names and projects removed.

“A purpose built security reverse proxy should be run in front of the app.”
Maintainer of an open-source project
“There is no built in brute-force protection ... [the app] is typically behind a proxy like nginx or Caddy”
Maintainer of an open-source project
“rate limiting and IP access rules can be configured in a reverse proxy”
Maintainer of an open-source project
“Our security model delegates authentication and ingress policy to a reverse proxy”
Maintainer of an open-source project
What the edge passed

The edge passed it. The guard stopped it.

22,293

blocked by Guard Core after the CDN let them through

8,257

blocked by the CDN on the same services

Of those 22,293 requests, 21,110 were refused with a 403 (most of them banned clients), 1,173 were detected attacks and 10 hit rate limits. The edge could not make those calls: it does not know which routes your app serves, its rules judge each request on its own without the app's record of what that client already tried, and its block ends at that one request instead of banning the client across every endpoint of the app.

The busiest banned client sent 5,359 requests in 15 days. The CDN forwarded every one.

What nothing stopped

Where there is no guard, nothing stopped it

22,897

requests for .env, .git and cloud credential files reached services in the same deployment that do not run Guard Core, through the CDN

They got a 404 only because those files were not there. One leaked .env is a full compromise, and nothing at the edge stopped the attempts.

Around the edge

A CDN protects your domain name, not your server's IP.

102,541

requests reached our server in 15 days without ever touching the CDN

Any server with a public IP receives direct traffic: scanners do not need your domain name. Whatever is listening on that address is exposed to it, however good the edge in front of the domain is.

fail2ban

Why fail2ban cannot help behind a CDN

fail2ban bans the client IP in the firewall. Behind a CDN, every request to your domain reaches the server from a CDN address, so a firewall rule on the client IP never matches. Only the app, which resolves the real client from trusted proxies, can ban it.

1,012

IPs banned by the SSH jail in 30 days, where it sees the real peer

128

IPs banned by the web jails in 30 days, bans that never match the requests they were meant to stop

0

bans by the bot-search jail: it reads the web server's error log, and the probes' 404s come from the apps, so it never sees them

No noise

No noise: we audited every detection

We replayed all 1,149 detections through the production versions. None was on a route the apps serve to their users. 52 are debatable by design: paths a crawler or browser can request on its own, flagged because the app does not serve them. 38 were triggered by a header or the body, which the web server does not log, so their exact trigger cannot be confirmed from the logs; all of them were on paths the apps do not serve.

1,149

detections audited

0

on a route the apps serve

52

debatable by design

Distributed attacks

One client per request looks harmless. A campaign does not.

A distributed attack spreads its requests over many addresses so that no single address looks suspicious: each one stays under every per-IP limit at the edge. In the demo below, no address sent more than 4 requests. Guard Core SaaS looks at the clients together. Campaigns score how alike their traffic is across six signals: shared request sequence, timing cadence, IP spread, endpoint spread, automation share and user-agent concentration. Coordinated Attacks flags many addresses sending the same attack to the same endpoint within minutes. Blocking a campaign from the dashboard adds every member IP to the project's IP blacklist. Both are Guard Core SaaS features on the Team, Business and Enterprise tiers; see pricing.

Here is a demo run on our production stack with documentation IP addresses: 40 identities, 4 requests each to the same 4 endpoints in a fixed order, 250 ms apart, with 6 rotating user agents.

160

requests sent

160

requests blocked

120

answered 403 because the client was already banned; 40 answered 400 when detected

The ban comes from the demo app's own rule, not from a default: the demo app bans after two detections. Rate limits never fired.

Outlook, not our data

Our logs do not show AI-driven attacks. These published reports describe where attacks are heading.

Check your own logs

Do not take our word for it. Run it on your logs.

These commands read your web server logs locally and print counts only: how many requests for secret files, admin panels and path traversal got each status code. If the edge stopped them, this prints nothing. They only read: run them on the server, and change the log path if yours live elsewhere.

nginx
zcat -f /var/log/nginx/access.log* | awk 'tolower($7) ~ /\.env|\.git\/|\.aws\/|\.ssh\/|wp-login\.php|xmlrpc\.php|phpmyadmin|\.\.\/|%2e%2e/ {print $9}' | sort | uniq -c | sort -rn
Caddy
zcat -f /var/log/caddy/access*.log* | jq -r 'select(.request.uri | ascii_downcase | test("\\.env|\\.git/|\\.aws/|\\.ssh/|wp-login\\.php|xmlrpc\\.php|phpmyadmin|\\.\\./|%2e%2e")) | .status' | sort | uniq -c | sort -rn
Front door and back door

Keep the front door. Lock the back door too.

A reverse proxy and a CDN lock the front door, and you should keep them: they are the right place for volume, TLS and DDoS protection. Guard Core locks the back door from inside the app, where the routes and the history of each client are known.

Method

How we measured, and what it cannot tell you

  • Window: 2026-10-05 to 2026-10-19 (15 days); fail2ban figures cover 30 days.
  • Sources: CDN analytics, web server access logs, fail2ban logs and container logs.
  • Privacy: client IPs are hashed with a random per-run salt that is never printed, and every output is a count. The page shows aggregates only.
  • Limits: 38 detections were triggered by a header or the body, so the logs cannot confirm their exact trigger. This is one deployment over 15 days.