Released

Howdah 1.3.0 is released — Layer 1 (request-shape limits) plus Layer 2 (input inspection, log-only). Howdah starts in detect mode: the critical limits block, everything else only logs. Read the coverage table for exactly what has been tested on your PHP version before you switch rules to block.

Home / Docs / Config editor

Build your Howdah config

Pick what you want, read what each setting does, and take the files as copy, download or one curl command. The TOML is checked by the same parser the extension uses, compiled to WebAssembly: what this page calls valid, Howdah 1.3.0 accepts.

Start from Your files ↓
Load existing…

Paste a howdah.toml or choose the file. It is parsed by the core first; if the core rejects it, you see the exact error and the form is left as it is.

Showing the shipped defaults. With JavaScript, the files on the right update as you change the controls, and each change is validated by the real core. Without it, this page is still the full reference: every setting, its default and what it does.

php.ini

Read once per PHP process at startup: per-pool, .htaccess or ini_set() values have no effect. Once howdah.config names a readable file, the file's mode wins over howdah.mode; this page keeps the two in step.

block refuses offending requests, detect logs only, off disables everything. The shipped default is detect. Critical limits block even in detect. Once howdah.config names a readable file, the file’s mode is used and this value is ignored.
Where JSON event lines are written; empty means the SAPI error log. The directory must exist and be writable by the PHP worker (a subdirectory of /var/log, not /var/log itself). This ini directive is the only way to set the log path.
Path of the TOML file below. If the file is missing or cannot be read, Howdah uses howdah.mode and the built-in limits. If the file is present but malformed, Howdah falls back to its safe defaults (block mode, default limits) and keeps serving, and phpinfo still names the file: validate before deploying.
[limits]

Any limit set to 0 is rejected by the core (0 would block every request); there is no "unlimited", set a large number instead.

Multipart body

Parts in one multipart body. 2× stock PHP max_input_vars (1000); a memory-exhausting flood needs tens of thousands of parts.
File parts in one body. No legitimate form uploads hundreds of files at once.
Header lines in any one part. Browsers send 1–3; best-effort lower bound (only Content-Disposition reaches the callback).
Bytes (name + value) of any one part header; 8 KiB tolerates long UTF-8 filenames. Best-effort lower bound.
Bytes of a form-field name. Field names are identifiers or array keys; 1 KiB is very generous.
Bytes of an uploaded filename. Filesystems cap names near 255 bytes; 512 allows percent-encoded forms.
Total body bytes Howdah will process for one request (256 MiB). Keep it above your PHP post_max_size.

Input shape

GET + POST + cookie variables in one request. 2× stock PHP max_input_vars: a real 1500-field form passes; a variable flood is dropped and logged.
Array nesting of one name (a[b][c] = 2). Real forms nest a handful of levels.
Bytes of any single variable name or array key. Standard severity: logs in detect, blocks in block.

Request headers

Request headers in one request. Browsers send 10–30 and proxies add a few. Counting needs $_SERVER to be read (auto_globals_jit).
Bytes (name + value) of any one request header; 16 KiB tolerates large cookies and auth tokens.
[rules]: per-rule mode

Critical rules block even when the mode is detect; Standard rules log in detect and block only in block mode. "inherit" leaves the rule at the global behaviour; off disables it entirely. Every rule name the core accepts is listed, so a loaded file never has a rule this form cannot show; the Layer 2 and 3 names are accepted by the core but not read by the extension, so they sit under their own heading.

Multipart

Input shape

Request headers

Layer 2 and 3 rule names: accepted by the core, no effect in the extension

The extension reads Layer 2 overrides only from [inspect.rule_modes] and sink modes only from each [sinks.<sink>] table (both are in the sections below). A [rules] entry for these names is valid TOML that changes nothing; they are listed so a file that contains one can still be loaded, and the load message says so.

Layer 2 names (no effect here)

Layer 3 names (no effect here)

[inspect]: input inspection

Layer 2 only logs by default and is independent of the global mode: a global block does not arm it. Signature inspection is best-effort and can be bypassed; use the log to find application bugs rather than relying on it. In block mode the request is refused after the script has run (see Enforcement timing in the configuration reference).

Master on/off for Layer 2 (input inspection).
Independent of the global mode: a global block does not arm Layer 2. detect logs only (the default). block is enforced after the script has run (see Enforcement timing in the configuration docs).
Look for SQL-injection shapes.
Look for path-traversal shapes.
Look for command-injection shapes.
Scan at most this many bytes of one value.
Per-request aggregate scan budget; values past it are not scanned.
Per-request value count budget; values past it are not scanned.
Per-class override of inspect.mode.
Per-class override of inspect.mode.
Per-class override of inspect.mode.
[sinks]: sink hardening

Layer 3 only logs by default and is independent of the global mode: a global block does not arm any sink. Set a sink’s allowlist first, then arm block. include/require and eval are PHP 7.0+ only. The sink argument is never logged.

Master on/off for Layer 3 (sink hardening).
Independent of the global mode: a global block does not arm any sink. detect logs only (the default). Set a sink’s allowlist before arming block.
system, exec, shell_exec, passthru, popen, proc_open. Unset inherits sinks.mode. Only a string first argument is inspected (proc_open with an array command is not).
Commands that are fine, one per line. Anchored literal strings with an optional single trailing *. "/usr/bin/convert *" also allows anything appended after the space, so prefer exact commands. A bare * allows everything.
Off by default (object unserialize is high-volume). detect logs classes not in allow_classes; block refuses them. Fill allow_classes from the detect log first.
Class names that may be unserialized, one per line.
include/require. PHP 7.0+ only (accepted on 5.x, nothing hooked). Inert until allow_paths is set.
Directories include/require may load from, one per line. Lexical prefixes: a symlink under an allowed directory is not resolved.
eval. PHP 7.0+ only. There is no allowlist: in detect every eval() logs; in block every eval() is skipped.
phar:// stream access (1.3). Off by default; a global or [sinks] block does not arm it. detect logs phar access outside allow_paths; block refuses it.
Realpath prefixes under which .phar archives may be opened, one per line; matched component-wise up to and including the .phar segment. Empty means every phar access is a hit in detect.
Outbound requests to hosts not in allow_hosts (1.3). Off by default; a global or [sinks] block does not arm it.
One per line: a hostname (exact, case-insensitive; no wildcards), a literal IP, or a CIDR such as 10.0.0.0/8 or fd00::/8 (full address required, so 10/8 is rejected). The core rejects wildcards and out-of-range prefixes.

What this page does and does not do

  • Validation is the extension's own Config::from_toml, built to WebAssembly from the same source as the release. The error text you see is the core's.
  • Nothing leaves your browser except the URL you copy. The fetch service at cfg.howdah.org keeps no copies and no logs of configs.
  • The [log] TOML table is not offered: the extension reads the log path only from the howdah.log ini directive.
  • A malformed howdah.config on a server makes Howdah fall back to its safe defaults (block mode) and keep serving; using this page is how you avoid finding that out in production. Source: docs/config.md.