The signals your WAF ignores

Cenros avatar
Cenros

The free signal nobody is looking at

We bypassed every check in your WAF. All of them. The one thing we can’t bypass costs nothing and requires zero JavaScript.

A real Chrome browser loading a page - here’s what your server sees in its access logs:

GET /page           -> 200 (12KB)
GET /css/main.css   -> 200 (8KB)
GET /js/app.js      -> 200 (45KB)
GET /js/vendor.js   -> 200 (92KB)
GET /img/hero.webp  -> 200 (124KB)
GET /fonts/inter.woff2 -> 200 (18KB)
GET /favicon.ico    -> 200 (1KB)

7 requests. ~300KB. For a single page.

A sophisticated bot, even one that passes your WAF, your toString checks, your behavioral detection, everything - here’s what your server sees:

GET /page -> 200 (12KB)

1 request. 12KB. That’s it.

Not for a technical reason. For an economic one.

Loading 300KB of resources per page like a real browser, multiplied by thousands of pages, on residential proxies billed per gigabyte.. that multiplies the cost of the operation by 25. No bot will survive long at x25 on bandwidth costs just to load images it will never look at.

It’s a structural contradiction. You don’t solve it with better code.

Meanwhile, server-side correlation is trivial. An IP making 100 GET requests for HTML pages and 0 GET requests for CSS/JS/images is a bot. A 20-line script on your access logs. Free. Passive. And structurally resistant.

Your six-figure WAF runs on enemy ground. Your access logs run on yours.

When was the last time someone checked the HTML/assets ratio in your logs?

Traffic has a circadian rhythm

A human browses fast in the morning. Routine, scanning, quick clicks. 1 to 3 seconds between pages.

In the evening they slow down. Deep reading, slow scrolling, pauses. 4 to 8 seconds.

At 3 AM, if they’re still up, it’s very slow. Fatigue, re-reading, hesitation.

A bot crawls at the same speed at 10 AM, at 3 PM, at 3 AM. Its inter-page delay distribution is flat. Same pace, same rhythm, regardless of the time.

This is a signal very few WAFs exploit. Most just check the average delay between requests. Almost none correlate that delay with the IP’s timezone.

And yet the check is trivial: you geolocate the IP, derive the local time, and verify that the browsing rhythm is consistent. A French IP crawling at 3 AM with 1.5-second delays between pages.. that’s not an insomniac, it’s a bot.

While testing, we bypassed this by assigning a “simulated timezone” to each IP and varying delays accordingly. Simulated morning = fast. Simulated evening = slow. But that adds a layer of complexity most bots don’t handle.

Traffic has a circadian rhythm. If your bot doesn’t, the good WAFs see it.

Why these signals hold

Two things the HTML/assets ratio and circadian rhythm have in common:

They live server-side. The bot can’t access them. It can’t falsify what your logs record. It can’t change the time your geolocation places it at. The turf is yours.

They exploit economics, not technique. The HTML/assets ratio holds because loading all resources costs too much. The circadian rhythm holds because simulating it correctly adds operational complexity. Every structural signal a bot has to simulate is an added cost. Stack enough costs, and the operation no longer makes financial sense.

Your WAF is a speed bump. Your logs are a wall. Combine the two.