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.

Step 1: Start learning

  1. Publish the site through the reverse proxy and give it a WAF policy (Publish an internal web server safely, Steps 1–6).
  2. In the policy, set Mode to Detect only and choose the Application profile if the site is one of the known applications (WordPress, Nextcloud, Joomla, Drupal, Roundcube, phpMyAdmin). The profile checks those applications' own paths, which are the most common source of false positives.
  3. Save to pending changes and commit.

From now on, every request is inspected and passes. Use the site normally, and let your users do the same.

Step 2: Read the readiness checks

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.

Learning and readiness: verdict, readiness checks, and the next step
Learning and readiness: verdict, readiness checks, and the next step

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.

Step 3: Resolve likely false positives

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.

Likely false positives and daily traffic
Likely false positives and daily traffic

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.

Top rules: what each rule means, how often it fired, and where
Top rules: what each rule means, how often it fired, and where

Step 4: See who attacks

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.

Attackers: who, from where, and how
Attackers: who, from where, and how

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.

Step 5: Run the attack self-test

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 self-test: every attack detected, normal requests pass, and what blocks at the current threshold
The self-test: every attack detected, normal requests pass, and what blocks at the current threshold

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.

Step 6: Block in stages

When the verdict is READY (or REVIEW and you accept the warnings):

  1. Block at threshold 20, then Save to pending changes and commit. Only requests with several strong detections are stopped: clear attacks, almost never a normal visitor.
  2. Watch the report for a week. If no new false positives appear, Lower threshold to 10.
  3. After another clean week, lower it to 5, the OWASP Core Rule Set's default. The site is then fully protected.

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.

Good to know

Troubleshooting

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