Configuration

Every setting is one environment variable

There are no config files. Every setting is an environment variable, and every variable has a default that holds up in production. Deploy an empty environment, then override only the variables you actually care about.

Boolean values
truthy on true 1 yes falsy off false 0 no

Anything outside this set — typos like "ture" — fails fast at startup, naming the variable. Empty equals unset: FOO=${FOO} substitutions that produce FOO= fall back to the default instead of refusing to start.

Server & workers

15 variables
Variable Default Description
0.0.0.0:80

Address and port for the main HTTP server.

/var/www/html/public

Root directory for serving files and PHP scripts.

(unset)

Single canonical entry script. Unset = direct file mapping, *.php = front controller, non-.php = SPA fallback; with worker mode — the worker bootstrap. Resolved against DOCUMENT_ROOT.

false

Enable persistent worker mode. Requires ENTRY_FILE pointing at a .php script.

10000

Maximum concurrent TCP connections. Also the ceiling for the default QUEUE_MAX_WAITING (half of it); a malformed value is a startup error. Keep it above PHP_WORKERS + QUEUE_CAPACITY + QUEUE_MAX_WAITING.

CPU / 2 (min 1)

Async I/O threads. 1 = single-threaded, N = fixed count, unset = auto.

sapi

PHP executor backend: sapi for real PHP, stub for benchmarking without PHP.

CPU / 2 (min 1)

Worker pool size. N = fixed pool, MIN:MAX = dynamic scaling, 0 = auto.

30

Seconds a dynamic worker stays idle before being retired. Retirement waits for a moment with nothing in flight, so in-flight requests are never cut short.

workers × 128

Max pending requests in the PHP queue; arrivals at a full queue wait up to QUEUE_WAIT_TIMEOUT_MS. 0 = auto. For MIN:MAX pools, initial workers = the minimum.

1000

How long a request may wait for a PHP worker before a 529 — one deadline covering the wait for a queue slot and in the queue. 0 = reject the moment the queue is full.

workers × 128, ≤ MAX_CONNECTIONS / 2

Max requests parked waiting for a queue slot; past it, immediate 529. Each waiter holds a connection and its buffered body. 0 = auto.

64 MiB

Max request-body bytes held by the waiting set; a body that would exceed it gets an immediate 529, bodyless requests keep waiting. 0 = auto.

(unset)

Memory ceiling per worker in MiB before recycling. 0 = unlimited. On-demand recycling via Worker::scheduleExit().

true

Populate $_GET, $_POST, $_COOKIE, $_FILES, $_SERVER and php://input before each script. Falsy = object API only (oxphp_http_request()).

Traffic & delivery

15 variables
Variable Default Description
5

Max seconds to receive HTTP headers after connect (Slowloris protection).

30

Max seconds to wait for in-flight connections during graceful shutdown.

0 (off)

Max requests per IP per window. 0 = off; a full PHP queue answers 529.

60

Rate-limit window duration in seconds.

(unset)

Path to the PEM certificate. Both TLS_CERT and TLS_KEY must be set to enable TLS.

(unset)

Path to the PEM private key.

1.2

Minimum TLS version: 1.2 or 1.3. Validated at startup even when TLS is off.

PHP_WORKERS_MAX × 4

Max simultaneous open streams per connection.

20

Max queued RST_STREAM frames before the connection is closed (Rapid Reset protection).

65536

Max total decoded header bytes per request.

20

Seconds between PING frames; 0 disables.

10

Seconds to wait for a PING reply before closing the connection.

30d

Cache-Control: max-age for static files. Accepts 30s, 5m, 2h, 30d, 1w, 1y, bare seconds or off. Replaces STATIC_CACHE_TTL.

off

mtime revalidation for the in-memory cache — checked at most every 3 s per file. Replaces STATIC_CACHE.

4

Brotli quality 0–11. 0 disables compression.

Security

5 variables
Variable Default Description
SAMEORIGIN

Clickjacking protection: SAMEORIGIN, DENY or off. Sets X-Frame-Options + CSP frame-ancestors on every response; application-set headers always win. Invalid values fall back to SAMEORIGIN with a startup warning.

(unset)

Trusted proxy CIDRs (or private). Real client IP from Forwarded / X-Forwarded-* via rightmost-non-trusted.

(unset)

Glob patterns whose .php files never execute via direct URI (/uploads/**). Matched before disk I/O — no existence oracle.

404

Response on a deny match: an HTTP status 400–599 or a /-prefixed PHP fallback script inside DOCUMENT_ROOT.

(unset)

Absolute paths under which symlinks may escape DOCUMENT_ROOT. Unset = no escapes allowed.

Logs, traces & profiles

34 variables
Variable Default Description
info

Log verbosity: trace, debug, info, warn, error.

(unset)

Per-request access log: all = every request, error = 4xx/5xx only, unset = off.

(unset)

Internal server (/health, /metrics, /config). Port-only values bind 127.0.0.1; unset = not started.

(unset)

CIDR allow-list for the internal server; outsiders get 403, health probes stay reachable. Loopback is not implicit.

(unset)

Directory with custom {status}.html error pages.

524288

Max body bytes for internal query endpoints (512 KiB).

false

W3C Trace Context propagation: reads traceparent/tracestate and forwards them to PHP via $_SERVER.

false

Enable OpenTelemetry span export. Implies TRACE_CONTEXT=true.

grpc

Export protocol: grpc or http/protobuf.

localhost:4317 / 4318

OTLP collector endpoint.

10000

Export timeout in milliseconds.

(unset)

Authentication headers: key=value,key2=value2.

oxphp

Service name in exported spans.

(unset)

Service version attribute.

(unset)

Extra resource attributes: env=prod,region=us-east-1.

parentbased_traceidratio

Sampling strategy: always_on, always_off, traceidratio and the parentbased_* variants.

1.0

Sampling ratio 0.0–1.0 for ratio-based samplers; out-of-range values are clamped.

false

APM: auto-instrumentation of 34 PHP functions (PDO, mysqli, cURL, Redis…), error capture, tracing SDK. Requires OTEL_ENABLED.

100

Slow-query threshold in ms; slower DB queries get oxphp.db.slow=true.

false

Record bind parameters in db.params spans. Recorded raw (not obfuscated), so this can put PII in traces — enable only where that is acceptable.

false

Master switch. Other PROFILER_* vars are still parsed, so typos surface at startup.

0.0

Probability 0.0–1.0 that a request is sampled.

false

Also sample requests to the internal server.

(unset)

Bearer token required for on-demand profiling via oxphp_profiler_*().

50000

Per-request span cap; longer profiles are truncated.

256

Max call-stack depth per sample (hard cap 65535).

/tmp/oxphp-profiles

Directory for on-disk profiles.

xhprof,speedscope

Formats written to disk: xhprof, speedscope, pprof, flamegraph.

10

Rate limit on profile files written per second.

100

Max profile files kept; older ones are pruned.

(unset)

Remote endpoint to POST profiles to.

xhprof

Wire format for exports.

(unset)

Bearer token sent with each export.

(auto-detect)

XHGui-compatible wrapping; unset = auto-detect from the URL.

Async & shared state

20 variables
Variable Default Description
off

Opt-in fiber-suspending replacements for blocking builtins. Categories: sleep (native sleep/usleep), streams (blocking tcp:// reads and stream_select). 1/true/all = every category.

0 (off)

Dedicated async worker threads. 0 = functions registered but throwing AsyncException on call.

ASYNC_WORKERS × 64

Max pending tasks in the async queue. 0 = auto.

256

Per-worker cap on concurrent task fibers; global in-flight cap = fibers × workers.

true

Master switch for the whole OxPHP\Shared\* subsystem.

100000

Global cap on all Shared entries; inserts past it throw CapacityException.

1 GiB

Global cap on estimated memory across all entries.

0.7

Start shedding lowest-priority work past this fraction of the caps.

true

Toggles the oxphp_shared_* Prometheus exposition.

true

Toggles the /__ox_shared/* introspection API.

true

Value previews in introspection responses; disable if previews could leak data.

16

BFS depth for cycle checks; raise for deep graphs.

10000

Edges walked during cycle checks; raise for dense graphs.

1 MiB

Per-value size cap; larger inserts fail fast.

64 MiB

Per-channel total payload cap.

false

A panic inside a Mutex/Once closure poisons the primitive permanently instead of best-effort recovery.

off

Lock-contention diagnostics: off, count or trace.

100

Polling interval for the lock-diagnostics sampler.

256

Per-string truncation in previews, in bytes, cut at a character boundary.

20

Entries sampled in /entry previews.

Presets

Example configurations

Starting points straight from the docs — copy one and adjust.

# verbose logs while you build and debug
LOG_LEVEL=debug
# record every request, not just errors
ACCESS_LOG=all
# one worker — deterministic, breakpoint-friendly
PHP_WORKERS=1
# local /health, /metrics and /config endpoints
INTERNAL_ADDR=127.0.0.1:9090
# re-read edited static files without a restart
STATIC_REVALIDATE=on
Inspect the live configuration

With the internal server running, /config returns the resolved configuration as JSON. TLS paths, INTERNAL_ADDR and ERROR_PAGES_DIR are scrubbed from the response.

curl -s http://localhost:9090/config | jq .

Zero configuration is a valid configuration

Every default above is chosen so an empty environment just works.