An Azure WAF policy can log every attack that hits it, render a dashboard full of red match events, fire alerts to your on-call, and block absolutely none of it.
That is not a bug. It is a policy in Detection mode behaving exactly as configured. The gap between what that dashboard implies and what the origin actually receives is one of the most common false greens in a production Azure estate, and it is invisible to anyone reading telemetry instead of measuring outcomes.
This post is the advanced companion to Azure DDoS testing, itself one environment-specific application of the complete guide to DDoS testing. The parent walks the whole Azure stack: protection tiers, what each control is for, the order to validate them. This one goes down a level, into the L7 edge where Front Door, Azure WAF, and Application Gateway meet, and asks a narrower question.
Azure does not give you one DDoS defense. It gives you five, each configured separately, each billed separately, each owned by a slightly different team on a slightly different change cadence. The interesting failures are not inside any one of them. They are in the seams between them.
Azure's defense is five products, not one
Walk the request path and count the independently configured controls a packet passes on its way to your application.
DDoS Protection guards the public IPs at L3 and L4, on the Network or IP tier you enabled per resource. Front Door absorbs and caches at the global anycast edge, with its own WAF policy attached. Application Gateway terminates HTTP regionally, with a different WAF policy attached. The Virtual Machine Scale Set behind it adds or sheds capacity on an autoscale rule written for some prior traffic shape. Azure CDN, if it is in the path, absorbs whatever is cacheable and forwards the rest.
Five controls. Five configuration surfaces. Five separate ways to be wrong.
Each one, tested alone, can pass. A volumetric flood gets absorbed. The WAF blocks a canonical payload. The origin rejects a direct connection. The scale set scales. Every individual check is green, and the stack still fails, because an attacker does not hit one control at a time. An attack lands on the whole path at once and flows to whichever seam is loosest.
A test that steps through the controls one at a time is testing five products. A test that drives the seams is testing your deployment. This post is about the second kind, and it starts with the seam that produces the most false greens: WAF policy mode.
Detection versus Prevention: the false green in Azure's own vocabulary
Every Azure WAF policy runs in one of two modes. In Detection, the engine evaluates each request against the managed Default Rule Set and your custom rules, records a match, and forwards the request to the origin unchanged. In Prevention, it does the same evaluation and then blocks the match with a 403.
The evaluation is identical. The logs are identical. The dashboards are identical. The only difference is whether anything is dropped, and that difference is the whole game.
This is the Azure-native instance of a pattern that recurs on every cloud edge: the block versus count distinction, the same shape as an AWS WAF rule left in COUNT or a Cloud Armor rule stuck in preview. Detecting an attack and enforcing against it are two different events, and Azure lets you do the first without the second.
Why a policy sits in Detection
Nobody deploys a WAF in Detection to be insecure. They deploy it in Detection to be careful.
A new managed rule set will false-positive against real traffic until it is tuned. Detection mode is the sanctioned way to watch what a rule would have blocked, read the logs, add exclusions for the legitimate patterns it catches, and only then promote to Prevention. It is a correct step in a rollout.
The failure is that the promotion never happens. The tuning window that was supposed to last two weeks becomes the permanent state of the policy. A year later the WAF is still meticulously logging every SQL-injection probe and credential-stuffing burst, and forwarding every one of them to the application.
The dashboard makes Detection look like Prevention
Here is why this survives so long undetected. In Detection mode, the WAF is loud. It generates a match event for every rule hit, populates the Azure Monitor workbook with attack analytics, and drives the same alerts a Prevention-mode policy would.
Someone glancing at the security dashboard sees a WAF that is clearly seeing the attacks. Matches are climbing, alerts are firing, the graphs are busy. It reads as a defense doing its job. What the dashboard does not show, because it is not designed to, is that every one of those matched requests got a 200 from the origin.
Telemetry measures what the control noticed. It does not measure what the control stopped. A WAF in Detection maximizes the first and zeroes the second, which is precisely the combination that looks safest and is not.
What the test has to measure
You cannot read policy mode off a graph of match counts, because Detection and Prevention produce the same graph. You have to observe the outcome at the origin.
The test submits canonical malicious payloads, the OWASP corpus the Default Rule Set targets, known bad-bot signatures, an HTTP flood against an expensive path, and confirms the response is a 403 from the edge, not a 200 from the application. A logged match with a delivered request is a Detection-mode policy no matter what the dashboard says.
Then it measures the interval that a mode toggle hides: time-to-mitigation. Even a Prevention-mode policy has a window between the first malicious request and the point where custom rules and rate limits are fully engaged. Reading match logs tells you the WAF saw the attack. Only driving traffic and watching origin responses tells you when, and whether, it started dropping it.
The chart below makes the two modes concrete. It is a simulation to show the shape, not measured data.
The Detection line tracks the attack because the mode forwards everything it matches. The Prevention line is flat because the same rules, in the other mode, drop it. The distance between the curves is the entire value of the WAF, and a test that reads logs instead of origin outcomes never sees it.
The two-WAF seam: Front Door and Application Gateway are configured apart
Azure WAF is not a single control. It runs in two places, on the Front Door profile at the global edge and on Application Gateway in the region, and the two are entirely separate resources with separate policies, separate rule capabilities, and separate modes.
That separation is the seam. A common Azure topology puts a WAF policy on Front Door and a WAF policy on Application Gateway, on the reasonable theory that defense in depth is good. But two policies mean two chances for the mode, the rule set, and the exclusions to disagree.
The failure mode is asymmetry. The Front Door WAF is in Prevention, tuned, and confidently blocking at the edge. The Application Gateway WAF behind it was stood up later, left in Detection, and never revisited. As long as all traffic arrives through Front Door, the edge policy carries the load and the gap is masked.
Then someone finds the Application Gateway's public frontend directly.
Application Gateway frequently has its own public IP. If that IP is reachable from the internet, an attacker sends requests straight to it, skips Front Door entirely, and is now inspected only by the regional WAF policy, the one still in Detection. The strong edge policy protected a door the attacker walked around.
The test enumerates both policies and confirms they agree: same mode, compatible rule sets, and (this is the load-bearing part) that the Application Gateway frontend is not independently reachable at all. The correct topology locks the gateway to accept traffic only from the Front Door service, which collapses the two-WAF seam into a single enforced path. Validating that lock is the same discipline as validating origin reachability, one layer in.
Rate limits are an assumption until you sweep them
Azure WAF rate-limit rules count requests from a client over a sliding window and trip a configured action when the count crosses a threshold. They are the control most often deployed with a round number and never exercised against it.
A threshold picked at deployment is wrong in one of two directions, and you cannot tell which without driving traffic across it.
Set too loose, it misses a distributed flood whose per-source rate stays under the line. This is the carpet-bombing case: thousands of sources each sending traffic that is individually unremarkable, aggregating to an attack the per-client limit never sees. Set too tight, it rejects legitimate bursts, users behind a shared CGNAT egress, a mobile app retrying, a partner batch job, and converts your rate limit into a self-inflicted outage.
The grouping key decides which failure you get. A rule keyed on client IP behaves very differently against a concentrated flood from a handful of addresses than against low volume smeared across a huge source set. A rate-limiting rule that has never been driven to its threshold is a parameter, not a defense.
A single fixed-rate probe finds one fact: whether that one rate is above or below the line. The test that produces a usable number sweeps the rate and the source count across the range, finds the knee where the rule actually engages, measures the real shed rate past it, and runs a realistic model of legitimate traffic alongside the attack so the false-positive rate the rule imposes is measured rather than assumed. Shedding an attack while also shedding your customers is not a mitigation; it is a different outage.
The adaptive-tuning seam: a learning control needs a test that adapts
Azure DDoS Network Protection does not apply a fixed threshold the moment you enable it. It profiles the normal traffic to each protected resource and tunes its mitigation thresholds to that learned baseline, a documented adaptive real-time tuning capability. The parent post covers the profiling window this opens on a freshly protected endpoint. The methodological point here is different.
A control that learns cannot be characterized by a test that does not.
A fixed-parameter script, the same vector at the same rate for the same duration every run, is the single easiest input for an adaptive control to handle. Hold a shape steady long enough and the baseline absorbs it as normal; the threshold that was supposed to catch it relaxes to accommodate it. The script reports "not mitigated," which a report renders as a pass, when what it actually measured is that the test was regular enough to be learned.
Validating a control that adapts requires a test that adapts back: one that escalates where the control absorbs, shifts vector where it re-baselines, and mixes in legitimate traffic to expose collateral, so the moving target is measured in motion rather than at a single frozen point. That is the argument made in full for a learning L7 detector in testing Google Cloud Armor Adaptive Protection, and framed as its own engineering discipline in the definition of adaptive DDoS testing. BlackNeuron's Patent-Pending Adaptive DDoS Testing is built around that real-time feedback loop; the value is what the loop reveals, an exposure window and a re-convergence gap a static run cannot produce, not any internal mechanism.
The Azure-specific consequence: test the adaptive tuning against a shifting profile, not a constant one, and test it on a newly protected resource, not only one that has been profiled for weeks. A thin baseline is the state an attacker gets for free right after a launch or a migration.
The seams the parent covers, in one line each
Two more seams complete the picture. Both are treated in depth elsewhere, so they get a pointer rather than a re-teach.
The origin behind Front Door only receives Front Door's protection if it cannot be reached any other way. A leaked origin IP, through historical DNS, a certificate-transparency entry at crt.sh, or a non-proxied subdomain, lets an attacker connect directly and bypass the WAF, the rate limits, and DDoS Protection in one move. On Azure the lock has two parts that work together: a network security group admitting only the AzureFrontDoor.Backend service tag, and validation of the X-Azure-FDID header at the origin, because the service tag alone admits all of Front Door, not just your profile. This is origin IP exposure, covered in how attackers bypass CDN DDoS protection.
Azure CDN, where present, only shields the origin for traffic it actually caches. A cache-buster query string turns a cached URL into origin load, forwarding the flood straight through, which is the seam explored in why the edge only absorbs the traffic it caches.
Authorization: the Azure simulated-DDoS gate
Azure governs simulated DDoS testing separately from ordinary penetration testing. It is expected to run through an approved DDoS simulation partner, scheduled in advance, and kept inside an agreed envelope. The reason is structural: Azure's own detection cannot distinguish your authorized test from a real attack, and unannounced high-volume traffic can trigger automated platform mitigations.
BlackNeuron is a DDoS testing firm, not an Azure partner, and this is not a claim of approved-partner status. The durable instruction is procedural, because the partner list and thresholds change: before any Azure DDoS test, read Azure's current simulated-DDoS policy, confirm whether your planned approach needs an approved simulation partner, and secure written authorization from the resource owner regardless. The broader mechanics of keeping a test from disrupting production sit underneath the Azure-specific rules, and the L7 traffic-shaping detail lives in application-layer DDoS testing.
FAQ
What is the difference between Azure WAF Detection and Prevention mode?
Both modes evaluate every request against the same rules and write the same match logs. Detection mode forwards the request to the origin after logging the match; Prevention mode blocks it with a 403. A policy in Detection sees every attack and stops none of them, while producing a dashboard that looks identical to a Prevention-mode policy. Only observing the response at the origin, not the match count, tells the two apart.
How do you test an Azure WAF policy?
Drive canonical malicious payloads and application-layer floods through the edge and confirm the response is a 403 from the WAF rather than a 200 from the application. Enumerate both the Front Door and Application Gateway policies and confirm they agree on mode and rule set. Sweep rate-limit rules across a range of rates and source counts to find the actual engagement point, and run legitimate traffic concurrently to measure false positives. Reading logs alone cannot confirm enforcement.
Can traffic bypass an Azure Front Door WAF?
Yes, by two routes. If the Application Gateway or origin has an independently reachable public IP, an attacker connects directly and skips the Front Door policy entirely. And if the origin IP has leaked, a direct connection bypasses the whole edge. The fixes are to lock the gateway to accept only Front Door traffic and to lock the origin with the AzureFrontDoor.Backend service tag plus X-Azure-FDID header validation.
Why does an Azure DDoS test need more than a scripted load test?
Because Azure's adaptive tuning learns your baseline, and a fixed script is the easiest input for it to absorb. Held steady, a scripted pattern drifts into the learned normal and the control relaxes around it, so the run reports a pass it did not earn. Validating an adaptive control requires a test that escalates and shifts based on how the control responds.
Do you need Azure's permission to run a DDoS test?
In most cases, yes. Azure treats simulated DDoS testing separately from standard penetration testing and expects it to run through an approved DDoS simulation partner on a scheduled basis. Confirm the current policy with Azure and get written authorization from the resource owner before generating any traffic.
Where the wiring fails, not the products
Azure ships correct primitives. DDoS Protection absorbs volume at the backbone, the WAF engine evaluates rules exactly as written, Front Door only forwards what reaches it. None of those products is the thing that fails.
What fails is a mode toggle nobody flipped back, two WAF policies that drifted out of agreement, a rate threshold set to a round number and never driven, an origin IP that leaked into a certificate log, a cache that forwards the one request it will not serve. Every one of those lives in a gap between two correctly configured controls, and every one is invisible to a test that checks the controls one at a time.
The reason to test the seams is that an attacker never sees your architecture diagram. They see a surface, and they push on it until something gives at the place two of your five defenses meet and quietly assumed the other one had it covered. The green check on each control is real. The question a test exists to answer is whether the checks are still true when the traffic hits all of them at once.
