The web application firewall (WAF) protects a website you publish through Korthveil's reverse proxy (see Publish an internal web server safely). It can block real attacks from the first day, but a WAF that blocks too early also stops normal visitors when a rule mistakes their input for an attack (a false positive).
Learning mode avoids that. The WAF first only watches (detect mode): every request reaches the site, and Korthveil records what would have been blocked. After a few days it tells you whether the site is ready, which rules need an exception, and when to start blocking. Blocking then starts gently and gets stricter week by week.
At a glance:
| Stage | Days | What you do |
|---|---|---|
| Baseline | 1–3 | Nothing: the site runs in detect mode and Korthveil learns normal traffic |
| Tuning | 4–8 | Review Likely false positives, Accept the narrow exceptions; include a weekend |
| Validation | 9–14 | Run the attack self-test; when the verdict is READY, Block at threshold 20 |
| Stricter | +1 week each | Lower threshold to 10, a week later to 5 (the OWASP default) |
Everything is under Security › Web application firewall, panel Learning and readiness, one page per site. Every change goes into the pending changes and takes effect when you commit; Back to detect mode undoes blocking at any time.
From now on, every request is inspected and passes. Use the site normally, and let your users do the same.
Learning and readiness opens with the verdict, the number of requests inspected, how many would have been blocked, and the self-test. Below it, a short line says which stage the site is in and what to do next.

The verdict comes from seven checks:
| Check | Passes when |
|---|---|
| Observation time | 7 days or more (a warning from 3 days) |
| Weekend covered | traffic was seen on a Saturday and a Sunday (otherwise a warning) |
| Traffic volume | 1,000 requests or more (a warning from 100) |
| Likely false positives | none open |
| Application paths | no would-be blocks on the profile's own paths |
| Inspection errors | none (otherwise a warning) |
| Attack self-test | run on the current policy, and passed |
Last 7 days changes the window the report looks at. Download report saves it as a web page for your records. Restart learning clears the statistics, for example after the site changed a lot.
Korthveil suggests an exception only when a rule fires on one path for at least three different clients: that looks like the site's normal use, not like an attacker. A single source is never suggested; it is more likely an attack.

For each suggestion, Accept adds a narrow exception to the policy: one rule, one path, one field (for example rule 942100 for ARGS:q under /search). Everywhere else the rule keeps protecting the site. Save, commit, and run the self-test again.
Some rules are never excluded, because they are the WAF's own scoring (for example 949110).
Top rules explains every rule that fired: what it detects, a hint for when it can be a false positive, and where it matched. Use it for exceptions by hand under Rule exclusions (rule, path prefix, and the one field), and write in Why what you did, for the next person.

Attackers groups the flagged requests by client: when, how often, which kinds of attack (SQL injection, cross-site scripting, file inclusion, command execution, scanners), which fields, the tool it claimed to be, and the paths it tried. Attack types and Top attacking countries summarise it.

To stop a country or a network outright, add a firewall rule with a country or address-list source: the WAF then never sees that traffic. A trusted monitoring tool or office network goes into the policy's Allowlist (trusted clients) instead, with a label, so its requests are marked and not counted as attacks.
Run attack self-test sends a set of known attacks and normal requests through the committed policy, inside the firewall, without touching the site:

The result also says which attacks would be blocked at once at the current threshold, and which only once the threshold is lower. It is stored with the policy: after any policy change, the check asks you to run it again.
When the verdict is READY (or REVIEW and you accept the warnings):
Back to detect mode returns to watching at any time, for example after a large update of the site.
When the checks fail you can still type ENABLE and press Block anyway, but you then skip the protection against false positives. Do it only for a test site.
| You see | Meaning | What to do |
|---|---|---|
| NOT READY: only 1 day(s) observed | Too early | Keep learning; 7–14 days are recommended |
| Attack self-test: the policy changed | The test is from an older policy | Run attack self-test again |
| Application paths: would-be blocks on known … paths | The application's own pages trip a rule | Accept the suggestions, or add a narrow exclusion from Top rules |
| A normal visitor gets 403 after blocking | A false positive | Open the block under Recent blocks, add an exclusion for that rule, path and field; or Back to detect mode |
| No suggestions although rules fire | Matches come from fewer than three clients | That looks like an attack; leave it, or exclude by hand if you are sure |