Configuration Keys¶
Every key the code reads, in one place. This page answers "what was that key called again" — press Cmd+F and search it.
For what a setting means and why you would change it, follow the link in the last column. Nothing here explains anything; that is deliberate.
The single most common mistake
FileStorage reads storage_file. FileRateLimitStorage reads file. They are different keys for the same idea, and getting it wrong does not error — the backend falls back to a hashed filename in the system temp directory, so the firewall appears to work while keeping its block list somewhere you did not choose and may lose on reboot.
Top level¶
| Key | Type | What it holds |
|---|---|---|
configs | list | Other files to merge in — paths, globs, URLs, {config_dir} / {presets_dir} tokens. Loading & Includes |
global | map | Site-wide behaviour. Table below |
storage | map | Where blocks are persisted. Table below |
connections | map | Connections declared once and referred to as %connection(name)%. Named Connections |
logger | list | Monolog handlers. Logging |
events | map | listeners: decision listeners, each {class, args, events}. Reacting to Decisions |
metrics | map | statsd: host, port, prefix, tags, rule_limit, or true for the defaults. Export Metrics |
challenge | map | Challenge flow settings. Table below |
tarpit | map | max_concurrent (default 5) and max_seconds (default 30) — see Tarpit |
plugins | list | The rules. Table below |
A configs: entry naming a file that does not exist empties the whole document
A missing include is a load failure, not a skipped line — every rule stops being configured. global.require_config: true turns that into a startup exception instead.
global:¶
| Key | Type | Default | ||
|---|---|---|---|---|
mode | string | block | block, log, exception, disabled, lockdown | Mode |
panic_file | string | unset | A file that overrides mode when it exists | Panic Switch |
banning_status_code | int | 400 | Status sent when a rule blocks | Status Code |
banning_message | string | built-in | Plain-text body template; {{request.id}}, {{block.status}} and {{block.rule}} are substituted | Banning Message |
block_page | bool or map | unset | An HTML page instead of the message: lang, title, heading, message, styles, stylesheet, or your own template | Block and lockdown pages |
banning_json | bool | false | Answer a client that prefers JSON with JSON | JSON for API clients |
repeat_offender_status | int | 0 | Status for a client already on the block list | Global |
add_to_expire | int | 3600 | Seconds added to a ban on each repeat hit | Global |
blocking_escalation | list | [] | Window/offense/duration rules that lengthen bans | Multiple Offenses |
behind_proxy | bool | unset | Assert whether a proxy sits in front | Trusted Proxies |
require_trusted_proxies | bool | false | Refuse to start without trusted proxies, from either source | Trusted Proxies |
trusted_proxies | list | unset | Proxies to trust during evaluation; ranges, REMOTE_ADDR, PRIVATE_SUBNETS. Ignored when the host sets its own | Trusted proxies from YAML |
trusted_headers | list | x-forwarded-for, -proto, -port | Which forwarding headers to believe from them | Trusted proxies from YAML |
require_config | bool | false | Refuse to start if any config input failed to load | Loading |
lockdown | bool | false | Refuse everyone but lockdown_allow | Lockdown |
lockdown_allow | list | (empty) | Addresses, CIDRs and start-end ranges still served. Empty serves nobody | Lockdown |
lockdown_status | int | 503 | Lockdown | |
lockdown_retry_after | int | 300 | Seconds in Retry-After; 0 omits it | Lockdown |
lockdown_message | string | built-in | Supports {{request.id}} | Lockdown |
lockdown_page | bool or map | unset | As block_page, for the lockdown response | Block and lockdown pages |
path_source | string | pathinfo | pathinfo (relative to the front controller) or script_name (the file the server ran). WordPress needs script_name; front-controller apps keep the default | Path Source |
base_path | string | unset | Where the application is installed, for script_name in a subdirectory | Path Source |
stale_source_error_after | int | 0 (off) | Seconds before an unrefreshed source is an error | Stale Rule Sources |
reverse_dns | map | unset | Who makes reverse-DNS verification's lookups: provider, providers, resolver, resolver_options, timeout_ms. Unset is PHP's own lookups | Reverse DNS |
storage:¶
| Key | Type | |
|---|---|---|
type | class | FileStorage, DatabaseStorage, RedisStorage, MemcachedStorage, InMemoryStorage |
config | map | Backend-specific, below |
| Backend | config keys | |
|---|---|---|
FileStorage | storage_file, offense_file | Paths; relative resolves against the YAML file |
DatabaseStorage | connection, storage_table, offenses_table, schema_check_probability | Storage |
RedisStorage | redis, instance | Requires ext-redis |
MemcachedStorage | memcached, instance | Requires ext-memcached. Range searches are best effort |
InMemoryStorage | — | Per-process; nothing survives the request |
SharedStorage | shared, local | A fleet-wide list with a local copy underneath. Each is a {type, config} block — Storage |
Every block-list backend also accepts record_request, which decides what a block record keeps of the visitor's cookies, headers, query and body. It defaults to an allowlist rather than everything.
Rate-limit counters are stored separately, under the RateLimit plugin's own metadata:
| Backend | config keys |
|---|---|
FileRateLimitStorage | file |
DatabaseRateLimitStorage | connection, schema_check_probability |
RedisRateLimitStorage | redis, instance, ttl |
CacheRateLimitStorage | adaptor (a pool class or a memcached:// / redis:// DSN), args, namespace, options, ttl |
InMemoryRateLimitStorage | — |
challenge:¶
| Key | Type | Default | ||
|---|---|---|---|---|
provider | string | required | math, altcha, turnstile, recaptcha, or a FQCN | Challenges |
secret | string | required | HMAC key for pass tokens. Startup fails if empty | Challenges |
path | string | /_firewall/challenge | The path a submission is recognised at, matched against getPathInfo() | Add a Challenge |
submit_url | string | path, prefixed with the request's base path | Where the interstitial's form POSTs to. Only set it if a proxy rewrites paths | Add a Challenge |
cookie_name | string | fw_challenge_pass | Pass-token cookie; empty disables cookie delivery. On Pantheon, start it with STYXKEY_ | Challenges |
header_name | string | X-Firewall-Challenge | Header an SPA can send the token in; empty disables it | Challenges |
notice | string or list | unset | Plain text shown on every challenge page, above the form | A notice on the challenge page |
page | map | unset | The page's lang, title, heading, intro, button, error_message, styles and stylesheet. Startup fails on an unknown key or an unusable value | Wording, language and styling |
ttl | int | 3600 | How long a pass lasts: the default for rules naming none, and the ceiling for every rule and every submission | How long a pass lasts |
audience | string | provider name | aud claim, to scope tokens between instances | Scoping tokens |
passes_valid_from | int or date | unset | Refuse every pass issued before this moment. One value, no storage | Withdrawing a pass |
revocable | bool | false | Consult the per-nonce revocation list, at one storage read per valid pass | Withdrawing a pass |
provider_options | map | [] | Per-provider settings, keyed by provider name | Challenges |
plugins: — one entry¶
| Key | Type | Default | |
|---|---|---|---|
plugin | class | required | The rule class |
response | string | block | allow, block, challenge, record, redirect, mark, tarpit — decides the bucket, and buckets beat weights |
weight | int | 0 | Order within its bucket; lower runs first |
enable | bool | true | |
metadata | map | [] | How the rule behaves. Below |
config | list | [] | The rule's own entries — addresses, paths, patterns |
metadata: — common to every plugin¶
| Key | Type | Default | ||
|---|---|---|---|---|
name | string | class name | What the log calls it. Name every rule | Plugins |
mode | string | enforce | log observes this one rule without enforcing it | Observe mode |
status_code | int | banning_status_code | Per-rule override | Status Code |
banning_message | string | banning_message | Per-rule override of the block message | Banning Message |
block_page | bool or map | block_page | This rule's block page, merged over the global one | Block and lockdown pages |
default_expiration_time | int | challenge.ttl on a challenge rule, else 3600 | Ban length, or pass-token TTL. On a challenge rule it is capped by challenge.ttl | Global |
record | bool | true | false refuses without writing to the block list. On a redirect rule the default is false and true opts in | Evaluation Order |
mark_as | string | the rule's name | The signal response: mark raises, so several rules can raise one | |
mark_header | string | — | Also set this header on the request when marking | |
redirect_to | string | — | Required by response: redirect. Never built from the request, so it cannot become an open redirect | |
redirect_status | int | 302 | 301, 302, 307 or 308 | |
default_key | list | [client_ip, rule_pattern] | What rate limits count by, for rules declaring no key: | |
active | map | unset | timezone (default UTC), days, hours, from, until — when this rule is awake | Time Windows |
sources | list | [] | Pull this rule's entries from elsewhere | Rule Sources |
challenge_provider | string | challenge.provider | Per-rule provider override | Per-plugin providers |
tarpit_seconds | int | — | Required by response: tarpit. Capped by tarpit.max_seconds | Tarpit |
config | list | — | Legacy alias for the entry's config: | Legacy format |
Identity-verifying plugins (User Agent, and any implementing IdentityVerificationInterface) add verify, verify_cache, verify_ttl, verify_negative_ttl, verify_suffixes, verify_claim_wait_ms, verify_slow_threshold_ms, verify_offline, verify_provider and verify_timeout_ms — see User Agent and Reverse DNS.
Every sources: option is its own table in Rule Sources, including allow_catch_all, which decides whether a source may contribute an entry matching every address, max_size / max_entries, which bound how much a refresh may bring in, and checksum / signature, which decide whether the fetched bytes are checked against what the publisher asserted.
metadata: — edge signal rules¶
EdgeSignal reads what a CDN decided. See Edge Signals.
| Key | Type | Default | |
|---|---|---|---|
provider | string | custom | cloudflare, fastly, custom |
headers | map | [] | Signal to header name; layers over the profile |
Signals: bot_score, verified_bot, ja3, ja4.
config: — reputation rules¶
Reputation and AbuseIpdb take a map rather than a rule list. See Reputation.
| Key | Type | Default | |
|---|---|---|---|
provider | string | http (abuseipdb on AbuseIpdb) | abuseipdb, http, or a class implementing ReputationProviderInterface |
threshold | float | 75 | Score at or above which the rule matches, on the provider's scale |
cache_ttl | int | provider's | How long a verdict is reused |
error_cache_ttl | int | provider's | How long a failed lookup is remembered |
cache_dir | string | temp dir | Where verdicts are cached |
on_error | string | fail_open | fail_open, last_known_good |
subject | string | client_ip | What to score: client_ip, post.*, header.*, query.*, cookie.* |
subject_hash | string | unset | Send a digest of the subject instead of the value, e.g. sha256 |
block_status | int | 403 | Status returned when the rule blocks |
block_duration | int | 3600 | How long the address is remembered |
api_key | string | unset | abuseipdb: required, or the rule is inert |
max_age_in_days | int | 30 | abuseipdb: how far back reports count |
when | list | unset | Conditions gating the lookup — same syntax as any rule |
upstream | string or map | required | http: the endpoint, as a source declares one. Must contain {ip} |
format | string | json | http: json, txt, csv, tsv, ndjson, yaml, xml |
score_path | string | http: dot path to the score in the decoded body | |
score_pattern | string | http: or a regex with one capturing group, read from the raw body | |
trusted_path | string | unset | http: dot path to an "allow this one" flag |
trusted_pattern | string | unset | http: or a regex for the same |
content_type | string | application/json | http: sent with an upstream.body |
provider_name | string | see docs | http: what log lines call the service |
public_only | bool | true | http: false also looks up private addresses |
timeout | float | 2.0 | Seconds to wait before giving up |
PHP constants¶
Set before Firewall::create(). These exist because they must be readable before any YAML is parsed.
| Constant | Default | |
|---|---|---|
KANOPI_FIREWALL_CACHE_DIR | system temp | Where compiled config and source caches are written |
KANOPI_FIREWALL_CACHE_TTL | 3600 | Default rule-source TTL, when a source names none |
KANOPI_FIREWALL_CACHE_MAX_AGE | 30 days | Compiled-config entries older than this are swept; 0 disables |
KANOPI_FIREWALL_CONFIG_FILE_CACHE | true | false (or any value FILTER_VALIDATE_BOOL reads as false, such as '0') stops the compiled config being written to files; a pool from Config::setConfigCachePool() is still used |
KANOPI_FIREWALL_CACHE_MAX_STALE | — | How long a stale cached source may still be served |
KANOPI_FIREWALL_CACHE_TIMEOUT | — | Fetch timeout for remote sources |
KANOPI_FIREWALL_REQUIRE_CONFIG | false | Same as global.require_config |
KANOPI_FIREWALL_SOURCES_OFFLINE | false | Never fetch on the request path. Also switches off reverse-DNS verification for rules that do not set verify_offline |
Environment Variables covers %env(...)% substitution, which works in any value above.