Ferron 3 change log
Stay updated with a Ferron 3 change log, featuring bug fixes, new features, and enhancements for each release.
Ferron 3.0.0-rc.3
Released in September 6, 2026
Added
FastCGI
- Support for multiple FastCGI backends: multiple FastCGI backends can now be configured using multiple
fcgiorfcgi_phpdirectives.
Changed
HTTP cache
- Automatic
_lscache_varyvary cookies: request cookies with names starting with_lscache_vary(or the_litespeed_varyalias) are now part of the cache key without explicit configuration, matching LiteSpeed behavior.X-LiteSpeed-Cache-Control: no-varydisables all cookie-based vary dimensions for the response.
FastCGI
fcgi_phpsubdirectives: thefcgi_phpdirective now supports same subdirectives asfcgi(exceptextensionandpass). This allows for more fine-grained control over PHP FastCGI requests.
Fixed
HTTP cache
- HTTP cache coalesce timeout fix: coalesce timeout in HTTP cache no longer causes server crashes.
- Default HTTP cache TTL fix: default HTTP cache TTL is now 10% of the time since the resource was last modified (RFC 9111) or disabled (LSCache compatibility). This improves cache consistency.
HTTP compression
- HTTP upgrade fix: HTTP dynamic content compression is no longer applicable for 101 (Switching Protocols) responses for HTTP upgrades to work correctly.
Ferron 3.0.0-rc.2
Released in September 4, 2026
Fixed
HTTP server core
- HTTP/2 empty body error fix: HTTP/2 requests with empty bodies no longer cause server errors. This is the only significant change in the emergency Ferron 3.0.0-rc.2 release.
Ferron 3.0.0-rc.1
Released in September 4, 2026
Changed
Automatic TLS
- ACME fallback improvements: Ferron now tries each configured fallback provider when the primary provider fails when provisioning a certificate (not just on account creation).
- ACME certificate installation: Certificates are now reused from cache early before ACME provisioning cycles to mitigate TLS certificate errors after reloading the configuration.
OCSP stapling
- OCSP status logging and metrics: added
ferron.ocsp.cert.statusattribute to log records and metric labels, allowing the certificate’s OCSP status (good,revoked,unknown) to be logged and monitored.
Observability
- No metric exemplars linking to sampled-out traces: metric exemplars are no longer linked to traces that are sampled out (and thus not exported) to avoid misleading observability tools and reduce debugging friction.
Reverse proxy
- Connection pool racing improvements: when the connection pool is racing and multiple connections are being established simultaneously, the connection pool now selects the connection that is ready first, avoiding unnecessary delays and improving overall performance.
Fixed
HTTP server core
- Backlog size fix: the default backlog size is now 4096 on Linux, and -1 on Windows, *BSD, and macOS. It was -1 on all platforms before this change (which would be equivalent to 0 on Linux).
- Response body streaming fix: response body streaming is now fixed to avoid buffering instead of streaming, improving performance and memory usage. This also allows Ferron to be used as a reverse proxy for web applications that use SSE (Server-Sent Events). (GitHub issue)
Automatic TLS
- Automatic renewal info fix: fixed an issue where automatic renewal information was not applied correctly, leading to confusion about certificate expiration.
Ferron 3.0.0-beta.11
Released in August 31, 2026
Added
HTTP server core
- Support for listening to multiple IP addresses: multiple IP addresses can now be specified in the
listensubdirective intcpblock for TCP and QUIC, allowing the server to listen on multiple selected network interfaces.
Forwarded auth
- Forwarded auth
intercept_errorssubdirective: addedintercept_errorssubdirective to forwarded auth stage, allowing custom handling of upstream error responses, similarly tointercept_errorssubdirective for reverse proxying. - Forwarded auth
request_headersubdirective: addedrequest_headersubdirective toauth_to, allowing headers to be added (+Name), removed (-Name), or replaced (Name) on the request sent to the auth backend, mirroring the reverse proxy’srequest_headersubdirective.
Changed
Runtime
io_uringdisabled by default:io_uringis now disabled by default in the runtime configuration, as small static file serving and reverse proxying performance turned out to be slower than withepoll.
Static file serving
- Symlink check performance optimizations: optimized symlink check performance by caching the results in file open cache along with opened files.
HTTP caching
- HTTP caching performance optimizations: performed multiple internal throughput optimizations to the in-memory HTTP cache store, especially on the cache hit path.
- Cache key fingerprinting improvements for observability: truncated cache key (up to 48 characters) with the query string removed, useful for diagnosing why a specific request missed. Optionally with a short non-reversible tag (
q=<16 hex chars>), and if the base itself had to be truncated, a second tag (h=<16 hex chars>). - Cache metrics improvements: cache decision reasons are now attached to access log entries and cache metrics.
Forwarded auth
- Forwarded auth
X-Real-IPheader: addedX-Real-IPheader to forwarded auth requests, with same value as reverse-proxied requests.
Fixed
Configuration
- Empty configuration validation: added a check for empty configurations (no ports, no global directives/matchers) to return an error instead of silently starting.
- Protocol + IP + port host block split fix: previously, the protocol+IP+port combination was not properly split when used as a host block (for example,
http 127.0.0.1:18081lead to two host blocks:http 127and0.0.1:18081).
HTTP server core
request.uriinterpolation fix: previously,request.uriinterpolation included the full request URL, including thehttp/httpsscheme and hostname. This had been changed to only include the path and query string.- HTTP status code for overlong paths: previously, overlong file paths led to 500 Internal Server Error status code to be returned. This has been changed to return 400 Bad Request instead.
Logging
- Reduced startup logs: removed multiple startup logs for various features to reduce noise in the logs.
Static file serving
- Static file serving symlink settings fix: previously, symlink mode (follow or not) was not properly respected when serving precompressed static files.
TLS
- Certificate and private key mismatch fix: previously, when a certificate and private key did not match for a given host, this was silently allowed. This has been changed to error out on the startup of the server.
Ferron 3.0.0-beta.10
Released in August 27, 2026
Breaking changes
HTTP server core
- Linear-time regular expression engine: The regular expression engine used by Ferron has been replaced by one using a linear-time algorithm, which means that backtracking regex patterns are no longer supported. This change was made to avoid potential ReDoS and catastrophic backtracking issues. If you are using syntax such as
(?= ...),(?! ...),(?<= ...),(?<! ...), or\1, you need to rewrite the regexes. - Symlink following disable by default for webroots: following symlinks during static file resolution from webroot is now disabled by default for symlink attack prevention. Check if web application uses symlinks (if it uses, use
disable_symlinks falseor a similar setting).
Automatic TLS
- DNS provider support reduced: The DNS provider support for automatic TLS DNS-01 challenge has been reduced to a minimal set of providers, to keep Ferron lightweight and lean. Supported providers are
rfc2136,route53,bunny,digitalocean,cloudflare,dnsimple,googlecloud,ovh,spaceship,porkbun, anddesec.
Configuration
enabled trueno longer effective:tls { ocsp { enabled true } }andbasic_auth { brute_force_protection { enabled true } }are no longer effective and are replaced withtls { ocsp }andbasic_auth { brute_force_protection }respectively, to improve consistency across configuration directives.
Added
CLI utilities
ferron-passwd --stdin:ferron-passwdcan now read passwords from the standard input when not using TTY. Passwords should be one per line.
HTTP server core
- Unix domain socket listeners (Unix only): added support for listening on Unix sockets for HTTP server, which can be configured using global
unixdirective. If Unix sockets are configured, Ferron disables TCP and QUIC listeners. - Support for h2c listeners: added support for h2c (HTTP/2 cleartext) listeners on top of the existing HTTP/1 and HTTP/2 implementations.
Reverse proxy
- Same-upstream retries: new
max_retries_per_upstream <count>directive controls how many times the proxy retries the same upstream on a transport or connection failure before it falls back to another backend viaretry_connection. The retry applies only to idempotent requests with a replayable body and, whenretry_budgetis enabled, each same-upstream retry consumes a retry-budget token. Default:max_retries_per_upstream 1.
DNS / Automatic TLS
commandDNS provider (external hook): the newdns-commandmodule provides acommandDNS provider for the ACME DNS-01 challenge. It runs an external program for each record change, passing the record details through environment variables (FERRON_DNS_ACTION,FERRON_DNS_DOMAIN,FERRON_DNS_RECORD_TYPE,FERRON_DNS_RECORD_VALUE,FERRON_DNS_RECORD_TTL). The program must exit0on success. Configure withprovider commandand thecommanddirective (with optionalmin_ttl, default60). This delegates DNS updates to any DNS server or automation that Ferron does not support natively.
Observability
- Baggage key promotion for StatsD: the
observability-statsdmodule now supports thebaggagedirective, which promotes W3C Baggage keys into DogStatsD tags (requiresdatadogmode). This matches the existing OTLP and Prometheus baggage promotion support.
Changed
HTTP canary
- Persistent affinity cookie by default: the
canarymodule now writes a persistent affinity cookie (7-dayMax-Age,HttpOnly,SameSite=Lax) whenset_cookieis enabled, instead of a browser-session cookie. A newcookieblock lets you configure the cookie lifetime and attributes (ttl,path,domain,secure,httponly,samesite), matching the HTTP proxy’s affinity cookie settings.
HTTP server core
- HTTP/3 enabled by default: HTTP/3 is now enabled by default in Ferron, as it’s also no longer declared experimental (it was declared such, because the previous HTTP/3 implementation,
h3, was declared experimental as well). - Interpolations allowed in static error page paths: interpolations are now supported in static error page paths served by the HTTP server, allowing for using them with A/B testing.
Observability
- Server and client port in error logs: the
client.portandserver.portattributes are now included in error log messages, so you can easily identify which client and server ports are involved in a given error. - Baggage key promotion max cap more effective: when baggage values exceed the maximum distinct value cap, they are dropped instead of being hashed (cardinality would be the same when hashed, except for hash collisions, so hashing would be ineffective).
Reverse proxy
- HTTP error resilience: when a backend fails while HTTP request is sent, if the request method is idempotent and body is not sent yet, Ferron will retry the request instead of bailing out.
Fixed
HTTP cache
- HTTP cache
Varycorrectness fix: a bug introduced in Ferron 3.0.0-beta.9 led to some header names inVaryheader values (If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,Range) being ignored by the cache, which might lead to wrong 304 responses being served. This has been fixed to properly handle these headers inVaryvalues.
HTTP server core
- Configured HTTP responses after HTTP -> HTTPS redirects: HTTP responses configured to be served after the
https_redirectstage will now only be served after a successful HTTP-to-HTTPS redirection. This ensures that these responses are not served before the client is redirected, which could lead to unexpected behavior or security issues. - Webroots with interpolations in URL rewrites: webroot paths can now contain string interpolations when using with URL rewrite rules.
- Less QUIC packet loss and retransmissions: previously, QUIC packet loss and retransmissions were higher due to improper routing of packets by the QUIC transport. This has been fixed to improve packet routing (via IP hashing when initializing and connection ID hashing later on) and reduce packet loss.
- HTTP/3 stream reset errors fixed with curl: fixed
curl: (18) HTTP/3 stream 0 reset by server (error 0x0 unknown)oncurlfor HTTP/3 in underlying HTTP server library used by Ferron. (GitHub issue) - IPv4 host blocks never matching: fixed IPv4 host never matching due to IP address canonicalization issues and IPv4-mapped IPv6 addresses.
- Best practice diagnostics for
locationblocks: the best practice diagnostics forlocationblocks with duplicate pathnames are now emitted correctly.
Configuration
- Stray
}fix: earlier, stray}characters inferron.conffiles could cause an infinite loop. These are now properly handled by erroring out.
Admin API
- Improved config redaction:
userssubdirective used by HTTP basic auth functionality is now redacted properly in the config JSON output.
TLS
- Local TLS with ALPN + OCSP: local TLS now supports ALPN and OCSP stapling properly, for HTTP/2 to work properly.
- OCSP stapling verification fix for some CAs: OCSP stapling verification now works properly for some CAs that have non-standard DER encoding of
tbsResponseData(like Actalis).
Observability and tracing
- High-cardinality metric removed: the
ferron.proxy.lb.selectionswith high-cardinalityferron.proxy.lb.scoreattribute has been removed to prevent exhaustion of metric storage.
Forward proxy
- Fixed
deny_ipsbypass using non-canonical IP representations: previously,deny_ipscould be bypassed using non-canonical IP address representations (such as0x7f.0.0.1or2130706433). This has been fixed to normalize the IP addresses first.
Static file serving
- Ranges beyond the end correctness: the server now returns
416 Range Not Satisfiablewhen a range request start exceeds the available content length.
Ferron 3.0.0-beta.9
Released in August 16, 2026
Added
CLI
ferron directivessubcommand: new CLI subcommand that prints every registered configuration directive as structured JSON, grouped by section. Useful for tooling, editor integrations, and inspecting the directive schema of the running binary.- FIPS build indication: a build compiled with the
fipsfeature printsThis build is configured to use FIPS-certified cryptography.in its version output.
Build & packaging
- FIPS-certified cryptography builds: the
fipsCargo feature enables FIPS-certified cryptography via AWS-LC and rustls. When enabled, OCSP stapling, TLS cipher suites and key exchange groups are restricted to FIPS-approved algorithms, and HTTP basic auth password verification supports only PBKDF2 (Argon2 and scrypt hashes are rejected). You can enable the feature withcargo build --features=fips, or through the build tooling:just build fips=true,cross-build/build.sh --fips, theFIPS=1Docker build argument, and the FIPS packaging scripts for archive, Debian, RPM, and Windows installer artifacts. FIPS artifacts are named with a+fipssuffix (for example,ferron+fips-<version>-<target>.tar.gz), and FIPS Docker images use the-fipstag suffix. - SBOMs for release archives: every release now ships a software bill of materials (SBOM) archive next to the binary archives, named
ferron-<version>-<target>-sbom.tar.gz(or.zipfor Windows). Each archive contains CycloneDX documents in JSON and XML format that list the third-party Rust crates compiled into the binaries. FIPS releases ship matching SBOM archives with the+fipssuffix. You can generate SBOMs locally withjust package-sbom <target> [fips=true].
Observability & tracing
- Configuration drift hints: new
ferron.admin.config_driftGauge metric andconfig_drift/config_drift_hints_enabledfields on the/statusAdmin API endpoint detect when configuration source files have changed on disk but have not been reloaded. Drift is detected via periodic lightweight mtime comparison (no re-parsing). Enabled by default; disable withdrift_hints falsein config adapter params (e.g.,--config-params "drift_hints=false"). A warn-level log is emitted when drift is detected, and an info-level log when drift resolves after reload. - StatsD metrics export: new
provider statsdinobservabilityblocks sends Ferron metrics to a StatsD server over UDP, withhost,port(default127.0.0.1:8125),prefix, anddatadogdirectives. The optionaldatadog truetoggle enables DogStatsD extensions: metric tags rendered from metric attributes (and control-plane metadata) and thehhistogram type. Without it, histograms are sent asmstimers (with seconds-to-milliseconds conversion). Theprefixdirective prepends a namespace to every metric name (e.g.,myapp.ferron.http.server.request_count:1|c). Each metric event is sent as a separate UDP datagram. (observability-statsd) - OTLP metric exemplars: the OTLP observability sink now supports exemplar attachments on metric data points, allowing the last sampled value to be attached as an exemplar. This allows for better trace correlation and debugging by attaching relevant historical context. Previously, this wasn’t supported due to OpenTelemetry SDK limitations, but the limitation was removed by replacing OpenTelemetry SDK with a custom OTLP exporter implementation.
- OTLP export batching tuning: the
logsandtracesOTLP signal sub-blocks now acceptexport_interval <duration>andexport_batch_size <number>, and themetricssub-block acceptsread_interval <duration>. This controls how often partially full batches flush and how many finished items trigger an export. - OTLP gzip compression: the
logs,metrics, andtracesOTLP signal sub-blocks now acceptgzip [bool](defaultfalse). When enabled, export requests are compressed with gzip (HTTPContent-Encoding: gzip, gRPC gzip compression). - OTLP exemplar toggle: the
metricssub-block now acceptsexemplars [bool](defaulttrue). Set it tofalseto stop attaching the last sampled measurement per series as an exemplar. - OTLP native histogram toggle: the
metricssub-block now acceptsnative_histograms [bool](defaulttrue). Set it tofalseto aggregate histograms with explicit bucket boundaries instead of the exponential layout. - Variable interpolation in log filenames: the
access_loganderror_logdirectives now support{{variable}}interpolation in file paths. Access log filenames use theaccesslog.prefix (e.g.{{accesslog.header_host}}) and resolve against access log event fields. Application log filenames use thelog.prefix (e.g.{{log.level}},{{log.error.type}}) and resolve against log event attributes. Environment variables are also available via theenv.prefix. This enables use cases like per-host access logs (access_log /var/log/ferron/{{accesslog.header_host}}/access.log). (GitHub issue)
HTTP server core
- Variable interpolation in map results: variable interpolations (
{{name}}) in map result values are now resolved at runtime. request.uri.query.<param>variables: therequest.uri.query.<param>variables are now available for string interpolations, so to not manually create a regular expression for extracting query parameter value from a query string.request.cookie.<name>variables: therequest.cookie.<name>variables are now available for string interpolations, so to not manually extract cookie values from the request.- CORS origin interpolations: CORS origin values can now be interpolated using
{{name}}variable syntax (previously they were declared invalid). - Request
Cache-Controldirectives: Ferron now honors the RFC 9111 §5.2.1 request directives on cache lookups.max-age=<n>andmin-fresh=<n>revalidate a stored response that does not satisfy the requested freshness window, andonly-if-cachedreturns504 Gateway Timeouton a miss instead of contacting the origin.no-transformis accepted and has no effect.no-cache(andPragma: no-cache) already forced revalidation;no-storealready bypassed the cache. - Canary deployments without a reverse proxy: new
canarydirective (http-canary) assigns each request a weighted variant with consistent-hash sticky affinity, for canary rollouts and A/B testing of static content. Affinity isipby default and can be based on a cookie, a request header, or a built-in request variable (hash). With theset_cookiesub-directive, Ferron generates a random sticky key and writes it to the affinity cookie itself when the request has none, so assignments survive client IP changes without help from the web application. Variants are exposed ascanary.variant,canary.weight, andcanary.keyvariables, so a document root likeroot "/srv/www/{{canary.variant}}"serves each variant its own content. Weights and variant lists can change on reload; clients near variant boundaries may move, other clients keep their variant. Observability includes theferron.canary.requestscounter (withferron.canary.nameandferron.canary.variantattributes), span attributes on theferron.stage.canaryspan, and theferron.canary.variantcustom access log field. (http-canary) - HTTP/3 protocol configuration directives: added several new
h3_*subdirectives inhttpdirective for fine-tuning HTTP/3 protocol configuration.
HTTP caching
- On-disk cache persistence: the HTTP cache can now survive process restarts. Set
persist <dir>in a cache block to write cache mutations to a journal and periodic snapshots under<dir>/<zone>. On startup, Ferron replays the snapshot and journal back into memory.persist_interval <duration>(default30s, minimum1s) controls how often Ferron flushes queued mutations. By default only public entries are persisted;persist_privateopts into persisting private entries. Deletions are always persisted. The persistence directory is single-process only — do not share it between instances. A SIGHUP config reload does not touch the on-disk cache.
Changed
HTTP server core
- PGO for GNU/Linux and some Linux with musl: PGO (profiled-guided optimization) is now enabled for GNU/Linux targets and some Linux targets with musl libc (64-bit x86, ARM64), for pre-built Ferron binaries. This mainly improves tail (p90/p99/max) latency for web requests.
.ferronfile extension:.ferronfiles are now supported as an alternative to.conf(which is a generic file extension) files for configuration (GitHub issue).- Panic hook improvements: panic hook has been simplified (removing backtraces that are likely unhelpful), and it now logs the Ferron version and build target.
- HTTP/2 and HTTP/3 implementations: HTTP/2 and HTTP/3 functionality in Ferron now depends on
vibeio-http’s in-house protocol implementations instead ofh2andh3from Hyperium. New HTTP/2 implementation features higher space savings for headers (~95%, versus ~91% with the previous implementation for a “Hello World” application), while new HTTP/3 implementation features improved protocol compliance (47/49 onh3spec, versus 44/49 with the previous implementation). - QUIC performance optimizations: QUIC “endpoints” are now per-thread instead of single-threaded, improving throughput and tail (p90, p99) latency for HTTP/3 requests.
Observability & tracing
- Non-existent directory handling for log files: when a log file path contains a non-existent parent directory, Ferron now creates it automatically before writing to the log file.
Configuration validation
- Diagnostic span location improvements: diagnostic span location in error messages is now more accurate, showing the exact line and column where the error occurred in the configuration file.
Container images
- “Slim” Debian-based Docker images:
ferronserver/ferron:3-debian(and other Debian-base image tags) now are based on a “slim” variant of Debian instead of the full Debian image, making the image smaller and removing unnecessary packages. The slim variant is still compatible with the full Debian image, so it should not break existing deployments.
Access control
- Interpolated strings as password hashes (HTTP basic auth): interpolated strings can now be used as password hashes for HTTP basic authentication (for example when moving from a static password to an environment variable).
- Password hashing backends for HTTP basic auth: password verification no longer depends on the
password-authcrate. PBKDF2 verification now uses aws-lc-rs, scrypt uses AWS-LC (EVP_PBE_scrypt), and Argon2 uses argon2-rs (the Argon2 C reference implementation) with constant-time comparison. Newly supported hash formats:$pbkdf2-sha384$and$pbkdf2-sha512$. Base64 salt and hash fields now accept both padded and unpadded encodings. PBKDF2 hashes accept a bare iteration count ($pbkdf2-sha256$600000$...) or the PHC parameter form ($pbkdf2-sha256$i=600000,l=32$...).
Fixed
HTTP server core
- Trailing slash redirects: trailing slash redirection logic now correctly handle index files (e.g.,
index.html) and redirects to the correct URL (with trailing slash) rather than serving two URLs (both with and without trailing slash) - Connection accept fix for
poll: previously, connections at TCP listeners (including HTTP/1.x and HTTP/2) accepted only a single connection when usingpoll(notepoll), stalling afterwards. This has been fixed to be able to accept multiple connections (seevibeiochangelog). - HTTP/2 host header handling correctness: previously, if both
hostand:authorityHTTP/2 headers were present, the web server would return a 400 error, which might not be correct according to the HTTP/2 specification (RFC 9113, section 8.3.1). This has been fixed to overrideHostheader value with:authorityheader value instead of appending a new value. - Symlink ownership check: previously, symlink ownership check (
disable_symlinks if_not_owner) was effectively a stub that effectively disabled all symlinks. This has been replaced with a proper implementation. - Conflicting Alt-Svc headers for HTTP/3: the server now rewrites Alt-Svc header values to remove conflicting HTTP/3 port definitions.
HTTP caching
- RFC 9111 compliance: updated freshness lifetime precedence, 304 revalidation header merging,
must-revalidatehandling,no-cache="field-names"support, and206 Partial Contentcacheability to align with RFC 9111. - Cache key & scope: cache keys now use resolved vhosts, normalize whitespace/sorting in
Varyvalues, and exclude client IPs for private responses. Added cardinality bounds for private keys and query string stripping from fingerprints to prevent cache fragmentation and information leakage. - Purge & security: scoped
PURGErequests to requesting hosts, added constant-timeX-Purge-Secretauthentication for propagated purges, and ensuredSet-Cookieheaders are never stored verbatim. - SWR & performance: improved
stale-while-revalidatewith SWR leader fresh response delivery, singleflight coalescing with timeouts, and throttled expired-entry sweeps. - Request handling:
no-storerequests now serve fresh entries (but don’t store), hop-by-hop headers are stripped, and fresh hits now answer client conditionals (e.g.,If-None-Match) with local 304s. - Metrics & diagnostics: corrected
Cache-Statuslabels forstale-if-errorand fixed double-counting in cache request metrics.
Observability
- OTLP gRPC
no_verificationTLS handshake: previously, when an OTLP gRPC endpoint was configured withno_verification trueand used HTTPS, TLS handshakes failed because the custom certificate verifier rejected TLS 1.2/1.3 handshake signatures. This has been fixed to return assertion-based verification results (matching the HTTP exporter behavior). ferron.ocsp.stapling.hit_totalmetric emission fix: previously, theferron.ocsp.stapling.hit_totalmetric emission didn’t function at all. It has been fixed to emit the metric to global observability sinks correctly.- ACME TLS resolution errors: previously, TLS resolution errors were always logged into the console when using automatic TLS via ACME. This has been changed to log them into configured observability sinks.
- Spurious abrupt connection termination error logs: earlier, some abrupt connections termination log were logged (
Reverse proxy: HTTP upgrade tunneling failed: peer closed connection without sending TLS close_notify: https://docs.rs/rustls/latest/rustls/manual/_03_howto/index.html#unexpected-eof), even if the connection was idle. This has been fixed along with an update to the HTTP server library used by Ferron (seevibeio-httpchangelog).
Configuration validation
- JSON configuration parse error reporting: previously, configuration parse errors were reported using human-readable error messages (even if the
--jsonflag was used). This has been fixed to report parse errors in JSON when configured to do so.
Reverse proxy
- Config reload cleanup: on configuration reload, old reverse proxy health check probe tasks are now aborted and per-config caches (resolved upstreams, retry budgets, unhealthy backend counters) are invalidated. Previously these accumulated on every reload, leaking background tasks and memory.
- Upstream resolution cache fix - in earlier versions of Ferron 3 beta, stale upstream resolution cache (with infinite TTL) could have caused upstream connection errors, due to drift between cached state and actual DNS state. This has been fixed by removing the infinite-TTL internal upstream resolution cache.
X-Forwarded-ForandForwardedheader value fix: previously,X-Forwarded-ForandForwardedheader values were truncated to 256 bytes maximum, which could lead to incorrect (or even malformed) client IP values in the proxy request. This has been fixed to set the header values correctly without truncation.
Static file serving
Rangerequest correctness: previously, weak ETags were included with206 Partial Contentresponses (and some other responses withRangerequest header), which is a violation of HTTP spec (RFC 7232, RFC 7233). This has been fixed to remove ETags from responses to range requests.
CORS
- CORS
Varyheader correctness - previously, if responseVaryheader was set upstream, it would be overwritten byVary: originheader, which might lead to incorrect caching. This has been fixed to appendoriginto the list of header names inVaryheader value instead. - CORS
Vary: Originheader: theVary: Originheader is now always added to CORS responses (instead of selectively). - CORS
Originrequest header fix: previously, ifcorsdirective contained non-*origin, Ferron didn’t add CORS headers at all. This has been fixed to add CORS headers for non-*origins as well.
CLI utilities
ferron-fmtduration string formatting fix: previously,ferron-fmtformatted short duration strings (like1s) without quotes, which caused subsequent parse errors. This has been fixed to always quote this kind of strings.
Ferron 3.0.0-beta.8
Released in July 15, 2026
Breaking changes
Configuration format
- Invalid escape sequences in quoted strings are now parse errors — previously, unknown escape sequences (e.g.,
\z,\$) were silently passed through. They now produce a lexer error. Use raw string literals (r"...") if you need literal backslashes in values (e.g., regex patterns).
Added
Configuration format
- Raw string literals — new
r"..."syntax for strings with no escape processing. Raw strings are ideal for regex patterns where backslashes should be treated literally (e.g.,r"^/api/v1(?:/|$)"instead of"^/api/v1(?:/|$)"). Raw strings do not support interpolation. - Line continuation — a backslash (
\) at the end of a line joins it with the next line, allowing long directives to be split across multiple lines (e.g.,proxy http://localhost:3000 \followed byhttp://localhost:3001). - Semicolons as optional delimiters — semicolons (
;) can now appear between statements and between host patterns as optional delimiters, providing compatibility with tools that generate semicolon-terminated output.
Changed
Configuration format
- Idiomatic configuration style — documentation now uses the new ferronconf idiomatic style: bare strings without quotes, bare boolean flags (e.g.,
io_uringinstead ofio_uring true), raw string literals for regex patterns (r"..."), and consistent 4-space indentation. - Expanded bare string character set — bare strings now accept
.,:,*,-,+,/,%,&,?,@directly without quoting, reducing the need for quoted strings in directive values.
Default pages
- Redesigned default error and directory listing pages — the built-in error pages and static directory listings have been redesigned to match new Ferron 3 branding (see https://ferron.sh/blog/ferron-new-look). The installation landing page (
wwwroot) has also been refreshed to match.
Fixed
Configuration validation
- Duplicate
observabilityblock warning fix — fixed duplicateobservabilityblock warnings (they’re now only displayed once) forferron doctor.
Ferron 3.0.0-beta.7
Released in July 11, 2026
Added
Observability & tracing
- Prometheus native histogram support — the Prometheus observability backend now supports OpenMetrics native histograms alongside classic bucket histograms. Enable with
endpoint_native_histograms truein theobservability.prometheusblock. Native histograms are only available in protobuf format; the text format continues to expose classic bucket histograms. Note that native histograms and exemplars are mutually exclusive for histograms — when native histograms are enabled, histogram exemplars are disabled. - Prometheus metric exemplars — the Prometheus observability backend now emits OpenMetrics exemplars on counter and histogram metrics when request trace context is available. Each exemplar carries the
trace_idandspan_idof the request that triggered the observation, enabling direct correlation between metric spikes and distributed trace flame graphs in compatible observability backends (Grafana, Prometheus 2.23+). Exemplars are enabled by default for counters. For histograms, exemplars are active only whenendpoint_native_histogramsisfalse(the default), since native histograms and exemplars are mutually exclusive. - Configuration content hash — new
ferron.admin.config_mtimeGauge metric andconfig_file_hash/config_file_mtimefields on the/statusAdmin API endpoint expose the xxh3 content hash and last-modified time of the loaded configuration files. This enables rapid cluster-wide drift detection via a single PromQL query checking for hash mismatch across instances. - DNS cache TTL remaining gauge — new
ferron.proxy.dns.cache_ttl_remaining_secondsGauge metric exposes aggregated remaining TTL for DNS cache entries (min,max,avgaggregation viaaggregationattribute). A companionferron.proxy.dns.cache_entriesGauge tracks the number of active cache entries. This allows operators to alert on impending DNS record expiration before backend connections break. - Pool connection limit metrics — the
ferron.proxy.pool.local_limitandferron.proxy.pool.global_limitpool metrics are now available for monitoring connection pool usage. - Method and idempotency labels on retry metrics — the
ferron.proxy.retry.countCounter andferron.proxy.retry.finalGauge now includehttp.request.method(categorized:GET,POST,PUT,DELETE,PATCH,HEAD,OPTIONS,CONNECT,TRACE,_other) andferron.proxy.method_idempotent(bool per RFC 9110 §9.2.2) labels, enabling zero-delay alerting on non-idempotent retry anomalies that indicate potential state corruption. - Selected backends per request metric — the
ferron.proxy.backends.selected_per_requestCounter metric is now emitted for every proxied request, withbackend_urlandbackend_unix_pathattributes. This allows operators to see traffic amplification effects when multiple backends are selected for a single request.
Security
- Rate limit throttling — the
rate_limitdirective now supports athrottlesubdirective that allows rate-limited requests to be delayed instead of rejected. When enabled, requests exceeding the configured rate limit are queued before being processed. This reduces the likelihood of client-side errors and improves user experience during temporary traffic spikes.
Reverse proxying
- Token-bucket retry budget — the new
retry_budgetdirective implements a token-bucket-based retry budget that limits retries to a configurable fraction of steady-state traffic. When enabled alongsideretry_connection true, retries consume tokens from a shared pool that is replenished by successful requests. If the retry budget is exhausted, further retries are refused and the request immediately returns503 Service Unavailable, preventing cascading retry storms from amplifying backend failures into a self-inflicted DDoS. Supportsmax_retry_rate(default: 10%),max_tokens(default: 10), andrefill_rate(default: 2.0 tokens/sec) subdirectives. New metrics:ferron.proxy.retry.budget_exhausted(Counter) andferron.proxy.retry.budget_tokens_available(Gauge).
HTTP server core
- JSON error responses — the new
json_errorsdirective in thehttp-jsonerrormodule enables structured JSON error responses for HTTP 4xx/5xx errors. Supports both RFC 9457application/problem+json(withtype,title,status,detail, and optionaltrace_idfields) and simpleapplication/jsonoutput formats. ThetypeURI is configurable (default:about:blank), with support for{status}placeholder substitution (e.g.,https://http.dev/{status}). Uselocation /api { json_errors true }to scope JSON errors to REST API endpoints. (http-jsonerror) - Client certificate common name variable — the
mtls.cnvariable is now available for interpolations in the server configuration, allowing to use the client certificate’s common name in routing rules and conditionals. - HTTP error page placeholders — custom error page files now support optional placeholder substitution. Enable with
error_page_placeholders true; the{{trace.id}}and{{trace.spanid}}placeholders are then replaced with the current request’s trace ID and span ID before the page is served. Substitution reads the file into memory, bypassing the zerocopy/sendfile optimization for those responses. - Static file resolution tracing — the file resolution process now emits a dedicated
ferron.pipeline.file_resolvetrace span withferron.file_resolve.request_path,ferron.file_resolve.root_path,ferron.file_resolve.outcome, andferron.file_resolve.resolved_path(success) orferron.file_resolve.last_candidate_path(error) attributes. Resolution error paths (403 Forbidden, 400 Bad Request) now includeferron.file_resolve.*fields in access logs, replacing the previous “black box” behavior where the file resolution process produced no observability signals. Directory listings now emitferron.static.dir_pathtrace span and access log attributes, and contribute to theferron.static.responsescounter metric withferron.static.outcomevalues"listing","listing_disabled","method_not_allowed", or"options".
Changed
Default pages
- Redesigned default error and directory listing pages — the built-in error pages and static directory listings have been redesigned with a modern, minimal, neutral style (system fonts, subtle accent gradient, responsive layout, and automatic light/dark mode). All styling remains inlined with no external assets. Default error pages now display the request’s trace ID when trace context is available, improving debuggability. The installation landing page (
wwwroot) has also been refreshed to match.
HTTP caching
- Unified cache eviction log summaries — the cache eviction log summaries are now unified to not include eviction reasons (they’re already in log attributes).
Observability & tracing
- Prometheus text format content-type — the Prometheus text endpoint now serves
application/openmetrics-text; version=1.0.0instead oftext/plain; version=0.0.4, matching the OpenMetrics specification used by prometheus-client. Prometheus scrapers that do not support OpenMetrics content negotiation may need to be updated. - Native exponential histograms for OTLP latency metrics — all OTLP histogram metrics now use Base2 Exponential Histograms instead of fixed linear buckets. This provides high-resolution percentile data across deep orders of magnitude (1ms to 100s), eliminating the need for manual bucket allocation and preserving tail-latency outliers that were previously masked by coarse bucketing. Affected metrics include
ferron.proxy.upstream.duration,ferron.proxy.pool.wait_time,ferron.proxy.tls.handshake_time,ferron.proxy.connect.latency,ferron.proxy.ttfb,http.server.request.duration, and all other histogram instruments exported via OTLP. - QUIC client/server address logging — QUIC connection error logs now include the client and server IP addresses, making it easier to trace connections and identify potential bottlenecks.
stage.namein traces renamed toferron.stage.name— renamedstage.nametoferron.stage.nametraces to ensure consistency with other Ferron-specific attributes.
Reverse proxying
- DNS thundering herd prevention — the DNS resolver now uses a singleflight mechanism to prevent multiple concurrent DNS queries for the same hostname, reducing load on DNS servers and improving performance.
- DNS resolution status metric attribute — the
metrics_resolved_ipdirective now also emits aferron.proxy.dns_statusattribute on all proxy metrics and access logs when enabled. Values:"resolved"(DNS succeeded),"nxdomain"(domain not found),"dns_error"(other DNS failure),"logical_dns"(resolution deferred to connect time),"static"(DNS not applicable). This provides visibility into DNS resolution outcomes without requiring per-IP label explosion.
Fixed
HTTP caching
- Duplicate eviction metrics — eviction metrics (and logs) were being emitted twice for cache hits, leading to incorrect metric values. This has been fixed.
Observability & tracing
- Prometheus endpoint is no longer on-demand — the Prometheus endpoint is now always available on the configured port, regardless of the number of active connections.
- Reverse proxy metric naming consistency — the
ferron.proxy.backend.excludedmetric name has been renamed toferron.proxy.backends.excludedfor consistency with other reverse proxy metrics.
Ferron 3.0.0-beta.6
Released in July 8, 2026
Added
Security
- Admin API bearer token authentication — the
adminblock now supports anauth_tokendirective for bearer token authentication on all admin API endpoints except/health. Clients must sendAuthorization: Bearer <token>header. The/healthendpoint is exempt to allow load balancer and orchestrator probes. - Prometheus endpoint bearer token authentication — the
observability.prometheusblock now supports anendpoint_auth_tokendirective for bearer token authentication on the/metricsscrape endpoint. Scrapers must sendAuthorization: Bearer <token>header.
Networking
- Multipath TCP (MPTCP) support — the
tcpblock now supports amultipathdirective that enables Multipath TCP on the HTTP listener. MPTCP allows a single TCP connection to use multiple network interfaces simultaneously, improving throughput and connection resilience. When enabled, Ferron attempts to create an MPTCP socket; if the kernel does not support MPTCP or it is disabled, a warning is logged and the listener falls back to standard TCP. (GitHub issue)
Observability & tracing
- Admin API request metrics — the admin API now emits per-request metrics (
ferron.admin.request.duration,ferron.admin.request.count) for all endpoints except/health, withhttp.request.method,url.path, andhttp.response.status_codeattributes. A dedicatedferron.admin.reload.countcounter tracks reload attempts. - Admin API structured logs — the admin API now emits structured log events through the observability pipeline for config reloads (Info/Error) and config queries (Info). Health checks and status queries are not logged to avoid noise.
- Prometheus scrape self-metrics — the Prometheus endpoint now emits
ferron_prometheus_scrape_duration_seconds(Histogram),ferron_prometheus_scrape_total(Counter), andferron_prometheus_scrape_errors_total(Counter) for monitoring scrape performance. - Process identity in OTel resources — the OTLP resource now automatically includes
process.pidandprocess.start_timeattributes, allowing observability backends to distinguish between concurrent and sequential process lifetimes. This prevents cumulative counters from adjacent process lifetimes from being visually interleaved in dashboards. - Status-code labels on cache store counters — the
ferron.cache.storesmetric now includes anhttp.response.status_codeattribute, allowing operators to directly query whether error responses are entering the cache layer without correlating timing across separate request and error series. - Cache key fingerprint in access logs — the cache module now contributes a
ferron.cache.key_fingerprintfield to HTTP access log lines, containing a truncated representation of the cache key. This simplifies debugging why a specific request resulted in a cache miss. - Singleflight coalescing traceability in access logs — the cache module now contributes
ferron.cache.coalesced(bool) andferron.cache.coalesce_wait_duration_ms(float) fields to HTTP access log lines, providing direct visibility into how long client requests are queuing inside the proxy during concurrent upstream revalidation. - Cache key components in OTel trace attributes — the
ferron.stage.cachespan now includesferron.cache.key.uri,ferron.cache.key.method, andferron.cache.key.evaluated_cookiesattributes, making trace flame graphs instantly diagnostic when evaluating hit-rate degradation caused by high-cardinality metadata variations. - Singleflight coordination metrics — new
ferron.cache.coalesced_requests(Counter) andferron.cache.singleflight_active_locks(Gauge) metrics expose the efficiency of the request deduplication layer, providing visibility into how effectively singleflight handles concurrent thundering herds. - Eviction reason structured logs — cache eviction events now emit debug-level structured log entries with
eviction.reason(ttl_expiredorcapacity_reached_lru),eviction.count, andferron.cache.zoneattributes, providing precise eviction timelines alongside the existing cumulative metrics. - Cross-plane traceability — the new
control_planedirective allows embedding arbitrary key-value metadata and static OpenTelemetry span links in the server configuration at global, host, or location levels. Metadata values are automatically included asferron.control_plane.*attributes on all observability signals (OTLP traces, logs, metrics, access logs) and as[key=value]prefixes in console and file logs. Span links establish causal relationships between control plane events and data plane traces without requiring a parent-child relationship. The most specific level wins (location > host > global), and metadata values support variable interpolation.
HTTP caching
vary_cookiesdirective — the cache module now supports avary_cookiesdirective that accepts an explicit list of cookie names for cache key differentiation. When set, only the listed cookies are included in the cache key, preventing high-entropy tracking or session cookies from fragmenting the cache and collapsing hit rates.
Changed
Reverse proxy
localhostresolution in reverse proxy —localhostis no longer exempt from STRICT_DNS-style DNS resolution; it is treated as a regular hostname and resolved via A and AAAA records.
Fixed
Reverse proxy
- Circuit breaker recording fix — recent failures are now recorded correctly in the circuit breaker state, allowing circuit breaker logic to function as expected.
Ferron 3.0.0-beta.5
Released in July 5, 2026
Added
Security
- Configurable symlink traversal protection — the new
disable_symlinksdirective allows configuration of symlink handling during static file serving (false,true, orif_not_owneron Unix). When enabled, symlinks are detected without following them during path traversal, preventing symlink-based escape attacks from outside the configured webroot. Defaults tofalsefor backward compatibility. - Error rate threshold — the
abuse_protectiondirective now supports anerror_rate_thresholdsub-directive that monitors HTTP response status codes. When a client triggers an abnormal number of configured error responses (e.g., 404, 403) within a time window, their IP is temporarily banned. This detects hostile scanning behavior such as probing for old vulnerabilities or non-existent plugin paths.
Observability & tracing
- Per-host TLS handshake metrics — new per-host metrics for TLS handshake duration (
ferron.tls.handshake.durationhistogram), handshake count (ferron.tls.handshake.totalcounter), and active connections (ferron.tls.connections.activeup-down counter). Each metric carriesferron.host,tls.protocol.version, andtls.cipher_suiteattributes, enabling per-host visibility into TLS performance and client compatibility. - TLS attributes on request traces — the
ferron.requestroot span now includestls.protocol.versionandtls.cipher_suiteattributes for HTTPS connections, correlating TLS parameters with individual requests without requiring a separate TLS span. - Per-host OCSP metrics — OCSP fetching metrics (
ferron.ocsp.fetches_total,ferron.ocsp.fetch_duration_seconds) now include aferron.hostdimension, and a newferron.ocsp.stapling.hit_totalcounter tracks per-request OCSP stapling outcomes per host. - Client and server IPs in error logs — structured error logs for bad requests (400), request timeouts (408), TLS handshake failures, and TCP connection errors now include
client.addressandserver.addressattributes, enabling IP-based correlation and troubleshooting. - Load-balancer selection score metric — new
ferron.proxy.lb.scoreGauge metric exposes the combined selection score used by thetwo_randomandp2c_ewmaload-balancing algorithms. For P2C-based algorithms, the two candidate scores compared during selection are captured and the winner’s score is emitted per request. Lower scores indicate more preferred backends, enabling direct visibility into traffic distribution without manual TTFB correlation. - Upstream response truncation detection — new
ferron.proxy.upstream.response_truncatedCounter metric and structured warning log detect when an upstream backend closes the response stream before the declaredContent-Lengthbytes have been forwarded. The proxy tracks bytes received during body streaming and flags truncation when the stream ends prematurely. A structured warning log includes the backend URL, bytes received, and expected content length, enabling immediate visibility into backend issues that return partial responses with valid HTTP status codes. This does not capture response bodies; only the byte count and content length are tracked. - Latency-aware circuit breaker — the
circuit_breakerdirective now supports an optionallatency_thresholdsub-directive. When configured, upstream responses exceeding this duration count as failures toward tripping the circuit, alongside transport failures and (optionally)5xxresponses. This allows circuit breakers to eject pathologically slow backends that return successful status codes. - Circuit breaker flapping detection — the
circuit_breakerdirective now supportsflapping_transitionsandflapping_windowsub-directives for detecting rapidly oscillating upstream backends. When an upstream transitions between circuit breaker states more thanflapping_transitionstimes (default: 3) withinflapping_window(default:10s), a single warning log is emitted and theferron.proxy.circuit.flappingGauge metric is set to 1. Individual transition logs are suppressed while the upstream is flapping. When transitions stabilize, a recovery log is emitted and the metric resets to 0. - Circuit breaker slow-start — the
circuit_breakerdirective now supports aslow_startduration sub-directive. When a backend transitions from half-open to closed (recovers), the load balancer applies a temporary virtual connection penalty that linearly decays over the configured duration (e.g.,slow_start 10s), preventing thundering herd by preventing the recovered backend from being immediately overwhelmed with new connections. - Configurable metric cardinality control — the
metrics_resolved_ipdirective (default:false) controls whetherferron.proxy.backend_resolved_ipis included in proxy metrics attributes. When disabled, metrics identify backends only by their configured URL, keeping cardinality low for environments with ephemeral IP addresses (e.g., Kubernetes). When enabled, each resolved IP becomes a distinct metric label value. This replaces the previous behavior of always emitting resolved IPs, which could cause cardinality explosion.
Reverse proxy
- Weighted load balancing for all algorithms —
random,two_random, andp2c_ewmanow support per-upstreamweightdirectives for proportional traffic distribution, joininground_robinandleast_conn. Higher weight values receive proportionally more requests across all five load balancing algorithms and session affinity. - Priority-based failover — backends can now be assigned numeric priority values to implement tiered failover. Lower values indicate higher priority. When all backends in a tier are unavailable, the next tier is used as a fallback. SRV upstreams support priority via an additive offset applied to DNS SRV priorities.
- SRV native weight support — DNS SRV record weights are now used for weighted load balancing within priority tiers. Each backend’s effective weight is
dns_weight × config_weight. The configweightsubdirective acts as a multiplier on top of DNS weights. - Strict DNS (A/AAAA) resolution — static upstreams with hostnames now resolve A/AAAA records via Hickory DNS by default, creating a separate backend per resolved IP for per-IP load balancing, circuit breaking, and health checking. IP literals,
localhost, Unix sockets, andlogical_dns trueupstreams are unaffected. Usedns_serversto specify custom resolver IPs. - DNS result caching — resolved DNS results for both strict DNS (A/AAAA) and SRV upstreams are cached in memory with TTL-based expiry derived from the DNS response TTL. This avoids redundant DNS resolution and lock contention for high-traffic hostnames. Cache entries expire based on the minimum TTL from the DNS response records, with a 30-second fallback when no TTL is available.
- HTTP upstream connection timeout — the
connection_timeoutsubdirective is now available for all upstreams, allowing configuration of the maximum time to establish a TCP connection to a backend. This prevents long hangs when backends are unreachable or slow to accept connections. - Health check logging — health check initialization logs now include the HTTP method and URI in the log output, making it easier to debug and understand which health checks are being performed.
- Upstream circuit transition logging — circuit breaker state transitions (open/half-open/closed) now log the upstream address and duration of the open circuit, making it easier to diagnose and troubleshoot circuit breaker behavior.
Observability & tracing
- Per-stage span attributes — all HTTP pipeline stages now emit stage-specific span attributes on their
ferron.stage.<name>spans, enabling detailed flame graph analysis without requiring metric export. Each module’s per-stage span carries module-specific context such as upstream backend URLs, cache results, rate limit decisions, authentication outcomes, and response status codes. See each module’s documentation for the full list of attributes and the tracing reference. - Upstream state in client traces — the
ferron.stage.reverse_proxyspan now includes upstream runtime state attributes (ferron.proxy.upstream.circuit_state,ferron.proxy.upstream.is_flapping,ferron.proxy.upstream.health_status,ferron.proxy.upstream.consecutive_failures,ferron.proxy.upstream.active_connections) correlating the selected backend’s health, circuit breaker, and connection state with individual client requests for OTLP trace debugging. - Resolved IP address attributes — reverse proxy metrics now include resolved IP addresses when strict DNS (A/AAAA) resolution is enabled. The
connect_tofield ofUpstreamInneris exposed asferron.proxy.backend_resolved_ipmetric attribute for all backend-related metrics, allowing observability of the actual IP addresses backends are connected to. This complements existingbackend_urlandbackend_unix_pathattributes. - Module-injected access log fields — all HTTP pipeline modules now emit debuggability attributes directly in access log lines, not just as trace span attributes. Proxy requests include
ferron.proxy.backend_url,ferron.proxy.connection_reused, andferron.proxy.retry_count. Cache results includeferron.cache.resultandferron.cache.zone. Rate limit decisions includeferron.ratelimit.resultandferron.ratelimit.zone. Auth outcomes includeferron.basicauth.resultandferron.fauth.result. Abuse protection includesferron.abuseban.action. Static files includeferron.static.file_path. CGI/FastCGI/SCGI backends include their respectivebackend_urlandscript_pathfields. See the logging reference for the full list. - Circuit breaker state in access logs — access log lines for proxied requests now include
ferron.proxy.circuit_breaker_state(closed,open, orhalf_open), and theferron.proxy.circuit.stategauge is now emitted on every proxied request instead of only on state transitions. This ensures circuit breaker visibility in both log queries and metric dashboards.
Changed
Static file serving
- FD-based metadata and file handle reuse — static file metadata is now obtained via
fstat-like system calls instead of a separatestat-like calls, closing the TOCTOU window between path validation and metadata read. A per-thread file descriptor reuse pool with preemptive bulk expired removal eviction is implemented for handle reuse, improving performance.HttpFileContextnow carriesfile: ReusedFileinstead ofmetadata: Metadata, so all consumers derive metadata from the validated file handle. - Path resolution without canonicalization — static file path resolution now uses component-level validation with simple PathBuf checks instead of costly filesystem canonicalization. This improves performance, eliminates path canonicalization overhead, and reduces the TOCTOU window by validating paths before symlink traversal checks. Combined with
disable_symlinks, this implements nginx-style defense-in-depth protection.
Reverse proxy
- Circuit breakers enabled by default — the
circuit_breakerdirective is now enabled by default for allproxyconfigurations. Circuit breakers track transport failures (TCP connect errors, TLS errors) and optionally upstream5xxresponses (whenrecord_5xx true), then temporarily eject unstable backends from the load balancer. This change improves resilience by protecting against cascading failures without requiring explicit configuration. To disable circuit breakers, setcircuit_breaker false. ferron.proxy.requestsmetric with backend URL — theferron.proxy.requestscounter metric now includes a backend URL (and related) attribute for all upstream requests, allowing observability of which backend URL handled each request.
Observability
- Canonical IP addresses in logs and traces -
client_ip_canonicalandserver_ip_canonicalare now available as OpenTelemetry-style log attributes and trace span attributes, replacing the legacyclient_ip/server_ip. - Custom log fields - custom log fields that have
.in their names are used as-is in OTLP logs instead of being converted to_in the log attribute name. This allows for more flexible log field naming and avoids conflicts with existing attribute names.
Fixed
Observability
- Consistent log targets -
tls-acmeandtls-httpnow useferron-tls-acmeandferron-tls-httplog targets respectively instead offerron_tls_acmeandferron_tls_http. - Circuit breaker gauge value fix - the
ferron.proxy.circuit.stategauge now correctly uses1foropenand2forhalf_open, matching the constant definitions. Previously these values were swapped.
Ferron 3.0.0-beta.4
Released in June 28, 2026
Added
HTTP caching
- Cache zones — the
cachedirective now supports azonesubdirective at host scope, allowing multiple hostnames to share a single in-memory cache store. Named zones can be pre-configured at global scope with custommax_entriescapacity. When a globalcache { max_entries }block exists without explicitzoneblocks, all hosts share a global zone by default. Hosts can opt out with an explicitzonedirective. - Generation-aware cache capacity —
max_entriesis now only evaluated on configuration reload instead of on every request, preventing premature LRU eviction when different host blocks specify different capacities for the same zone. - Zone attribute in cache metrics — all cache metrics (
ferron.cache.requests,ferron.cache.entries,ferron.cache.stores,ferron.cache.evictions,ferron.cache.purges) now include aferron.cache.zoneattribute identifying the zone the request belongs to.
Rate limiting
- Rate limit zones — the
rate_limitdirective now supports azonesubdirective at host scope, allowing multiple hostnames to share the same rate limit token bucket registries. Named zones can be defined at global scope. When a globalrate_limitblock exists without explicitzoneblocks, all hosts share a global zone by default. Hosts can opt out with their ownrate_limitblock. - Key extractor in fingerprint — the rate limit fingerprint now includes the key extractor type (
ip,uri,header), so rules with different key types no longer share the same registry. - Zone attribute in rate limit metrics — rate limit metrics (
ferron.ratelimit.rejected,ferron.ratelimit.allowed) and structured log events now include aferron.ratelimit.zoneattribute identifying the zone the request belongs to.
Static file serving
If-Rangesupport — TheIf-Rangeheader is now supported for conditional range requests (RFC 7233 §3.2), allowing clients to receive a partial response when the entity tag or modification date matches, or a full response when the representation has changed.
Fixed
Reverse proxy
- SRV priority/weight swap fix — SRV upstream resolution was reading the priority and weight fields in the wrong order, causing traffic to be routed to the wrong priority group. Priority and weight are now correctly interpreted per RFC 2782.
- Upgrade connection pool leak fix — HTTP 101 upgrade connections (WebSocket, etc.) were not decrementing the pool outstanding counter, permanently reducing available pool capacity by one per upgrade. The pool item now drops correctly, releasing the slot.
- Hop-by-hop header stripping on requests — the outgoing proxy request now strips hop-by-hop headers (
Connection,Keep-Alive,Transfer-Encoding,TE,Trailer,Proxy-Authorization,Proxy-Authenticate) per RFC 7230 §6.1, preventing clients from injecting these headers to influence backend behavior.
Static file serving
- HTTP range requests fix — HTTP range requests now correctly handle out-of-bounds ranges, returning
206 Partial Contentwith the available range instead of416 Range Not Satisfiable(per RFC 7233 §2.1). - On-the-fly compression allowed while using precompressed files — When serving precompressed files, the content is now compressed on-the-fly if precompressed files are not available, improving performance and reducing disk usage.
- Multipart byterange fix — Multipart range responses now include the required
bytesprefix inContent-Rangepart headers (per RFC 7233 §4.1) and correctly serve one additional byte per range to match inclusive-to-exclusive bounds (is_end_streamno longer signals end-of-stream while the last range’s data is still being streamed). If-None-MatchPOST fix — POST requests with a matchingIf-None-Matchheader now correctly return412 Precondition Failedinstead of200 OK(per RFC 7232 §4.2).If-Match: *with non-GET/HEAD fix —If-Match: *now correctly passes for POST and other non-GET/HEAD requests when a representation exists, per RFC 7232 §3.1.- Invalid Range header handling — Syntactically invalid
Rangeheaders are now treated as absent (returning200 OK) instead of416 Range Not Satisfiable, per RFC 7233 §3.1. file_cache_controlpanic fix — Malformedfile_cache_controlconfig values can no longer panic the request handler; a validation warning now catches invalid characters.- Missing
Content-Lengthfor compressed responses — Non-identity (compressed and precompressed) file responses now include the correctContent-Lengthheader. bytes_sentmetric fix — Thebytes_sentmetric now reports the actual compressed file size for precompressed responses instead of the original file size.
HTTP caching
- Cache revalidation fix - when a cached response is revalidated, the HTTP status code is now preserved from the cached response instead of always using
200 OK. - Abuse protection fix - 403 Forbidden responses generated by abuse protection module are no longer cached (as they are served before the cache).
- Cache scope fix - in the previous version (Ferron 3.0.0-beta.3), caches were per-host instead of global, even when maximum cache items subdirective was global-only. This has been fixed to restore the behavior before 3.0.0-beta.3.
- Stale-while-revalidate inflight request fix - when a stale response is revalidated, the inflight request handling no longer causes possible hangs.
Expiresheader in past edge case fix - a response with a pastExpiresheader and noCache-Controldirectives was incorrectly cached for 5 minutes. This has been fixed to cache the response for 0 seconds (expires immediately) instead.- Stale-if-error correctness fix - a stale response is no longer served when
must-revalidateis set, per RFC 9111 §4.3.4. - Cache key fix - an attacker could craft a cookie value containing
&cookie:to collide with another user’s cache key. This severe cache collision was fixed by using\0as the separator instead of&in the cache key. - 304 revalidation TTL fix - when a cached response is revalidated via a
304 Not Modifiedresponse, the stored response’s TTL,stale-while-revalidate,stale-if-error, andmust-revalidatedirectives are now updated from the 304 response’sCache-Controlheaders per RFC 9111 §4.3.4. Previously onlyETagandLast-Modifiedvalidators were updated, causing the original TTL to persist even when the upstream sent a differentmax-age. - Cache variant metadata leak fix - the
variants_by_basemap used for cache lookups previously grew without bound, retaining variant records for all URLs ever cached even after entries were purged. This map is now cleaned up when all entries for a base key are removed via purge.
Ferron 3.0.0-beta.3
Released in June 26, 2026
Breaking changes
If you are upgrading to this beta version, you must update your configuration files to accommodate the following syntax refactors:
- Removed
force_tracedirective - theforce_tracedirective has been removed as trace context creation is now always enabled for every request. Configurations usingforce_trace trueshould remove the directive; configurations usingforce_trace falseshould also remove it since the behavior now matches the default. - Default text log format changed - the default text access log format has been changed from Combined Log Format to Enhanced Combined Log Format, which adds
Hostheader and trace ID fields. If you rely on the default text log format and want to keep the previous behavior, explicitly set the pattern to the Combined Log Format usingaccess_pattern "%client_ip - %auth_user [%t] \"%method %path_and_query %version\" %status %content_length \"%{Referer}i\" \"%{User-Agent}i\""in yourlogblock. - Circuit breaker
5xxresponses no longer counted by default - upstream5xxresponses no longer trip the circuit breaker unlessrecord_5xx trueis explicitly set. This makes the circuit breaker purely transport-failure-based by default, giving operators explicit control over Layer 7 error sensitivity. passive_checkdirective removed - thepassive_checkdirective has been replaced with a more genericcircuit_breakerdirective that supports passive health checking and circuit breaking for both HTTP and gRPC services.
Added
Modules
http-variables- a new module for setting interpolation variables based on request conditions and mapping variables to custom access log fields.
Security
- Custom abuse event registration - it’s now possible to register custom abuse events, for example, for honeypots.
Observability & metrics
- Reload metrics and logs - the new
metrics-reloadmodule emits application log events and aferron.reloadscounter metric around configuration reloads, with aferron.reload.successfulattribute indicating success or failure and anerror.messageattribute on failures. - Abuse ban logging - the abuse ban logger now logs at the WARN level when an IP is triggered for banning, including the banned IP address and reason.
HTTP server core
set_vardirective - new directive for setting interpolation variables based on regex matching against request attributes, with optionalvalue,case_insensitive, andnegatesubdirectives. (http-variables)log_fielddirective - new directive for mapping any resolvable variable (including interpolated strings) to custom access log field names, evaluated after the response is generated. (http-variables)- Support for date-based caching - the
If-Modified-Sinceheader is now supported for static file responses, allowing clients to cache responses and avoid unnecessary re-downloads when the file has not been modified. - Auth user variable support - the
auth.uservariable is now available in variable interpolations for the HTTP server. - Trace ID and span ID variable support - the
trace.idandtrace.spanidvariables are now available in variable interpolations for the HTTP server.
HTTP caching
- RFC 9111 conditional request revalidation - when a client sends
Cache-Control: max-age=0orno-cache, the HTTP cache now performs conditional revalidation using storedETagandLast-Modifiedvalidators instead of bypassing the cache entirely. If the upstream returns304 Not Modified, the cached response body is served with fresh headers. If the upstream returns200 OK(content changed), the cached entry is replaced. (http-cache) stale-while-revalidatesupport - when a cached response’smax-agehas expired but astale-while-revalidatedirective is present, the stale response is served immediately to the client while the cache entry remains available within the stale window. This avoids latency spikes for expired cache entries. (http-cache)stale-if-errorsupport - when an upstream revalidation request returns a5xxerror and astale-if-errordirective is present on the cached response, the stale cached response is served instead of the error. This provides resilience against backend failures. (http-cache)must-revalidateandproxy-revalidateenforcement - entries withmust-revalidateorproxy-revalidatedirectives (ors-maxage, which impliesproxy-revalidate) are never served stale, even withinstale-while-revalidateorstale-if-errorwindows. (http-cache)- Ignore request cache control - when
ignore_request_cache_controlincacheis enabled, request-based cache control (e.g.,Cache-Control: no-cache) is ignored in favor of the configured cache policy. - Cache purge propagation - when a cache purge occurs (via
PURGEmethod orX-LiteSpeed-Purgeheader), the purge can be propagated to other instances via an external control-plane service. Edge instances send webhook POST requests to a configured control-plane URL, which broadcastsPURGErequests to all other registered edges. Loop prevention is handled via theX-Purge-Source: propagationheader and origin exclusion. (http-cache)
Automatic TLS
- HTTP auth for on-demand ask endpoint - when
on_demand_ask_authis configured, HTTP Basic Auth credentials are used for the on-demand ask endpoint, allowing for secure access to the endpoint. - Fallback ACME providers — the
fallbackdirective allows configuring multiple ACME providers with sequential failover. When the primary provider fails (e.g., CA outage), Ferron automatically tries the next configured fallback. Each provider maintains its own cached credentials. (tls-acme)
HTTP TLS provider
- On-demand certificate fetching — the
tls-httpmodule now supports on-demand (lazy) certificate retrieval. Whenon_demand trueis set in a wildcard host block (e.g.,*:443), certificates are fetched on the first TLS handshake for each SNI hostname, rather than polling a single endpoint at startup. - On-demand approval endpoint — the new
on_demand_ask,on_demand_ask_auth, andon_demand_ask_no_verificationdirectives control an authorization endpoint that is consulted before fetching a certificate for a hostname. The hostname is sent as a?domain=<encoded>query parameter. - Per-SNI certificate refresh — on-demand fetched certificates are individually refreshed at the configured
refresh_interval, ensuring certificate rotation is applied per hostname without affecting other SNIs.
Utilities
- Formatter utility - a new
ferron-fmtcommand-line tool is available for formatting and validating Ferron configuration files.
Changed
Admin API
- Reload failure status - the
/reloadendpoint now returns an error message in the response body when reload fails. Previously, it only indicated that reloading was initiated without any details about why it failed.
Observability & logging
- Improved error reporting in structured logs - reverse proxy, HTTP, TCP and QUIC error logs now include an
error.messageattribute when available. Also, some of the reverse proxy error log summaries being overly long have been fixed. - OCSP response caching log improvements - OCSP response caching logs are now emitted at info level by default. The
ferron.ocsp.cert.primary_sanattribute is also included in the log message when available. - Trace context always enabled - the
force_tracedirective has been removed, as trace context creation is now always enabled for every request. Additionally, when no tracing export is configured, Ferron will not generate a new span ID for incoming requests, avoiding unnecessary overhead.
TLS & ACME
- ACME provisioning cycle log improvements - ACME provisioning logs are now no longer emitted at debug level when a certificate is still valid or loaded from cache, as these messages were redundant with the
ferron.acme.domainsattribute already included in the main log message.
Proxying
- Forward proxying CONNECT cycle log improvements - CONNECT connection closure logs are now no longer emitted at debug level when a tunnel is closed to reduce noise in the application logs.
- Circuit breaker
record_5xxtoggle - thecircuit_breakerdirective now supports arecord_5xxsubdirective (default:false) to control whether upstream5xxresponses count toward tripping the circuit.
Fixed
Observability & logging
- HTTP/2 and /3 host header log visbility fix - HTTP/2 and HTTP/3 requests now log the
hostheader correctly, ensuring visibility into the host name of the server that handled the request.
TLS
- OCSP stapling ECDSA issuer fix - when using ECDSA certificates for OCSP stapling, the issuer OID was not being correctly extracted from the certificate, leading to incorrect validation. This has been fixed to ensure the issuer OID is extracted correctly and used for OCSP validation.
- TLS certificate provider inheritance fix - When using TLS certificates provided by a parent configuration, the certificate provider was incorrectly inherited, leading to errors when attempting to use these certificates in child configurations. This has been fixed to ensure that the certificate provider is correctly inherited and available for use in all child configurations.
HTTP server core
- Error configuration routing fix - error configuration routing were resolved with a wrong configuration, when using a wildcard hostname in the configuration, even when no error configuration is set. This has been fixed to ensure wildcard hostnames are correctly handled.
Server startup
- Critical Unix daemon initialization failure fix - when initializing as a Unix daemon, the forked process would exit due to logging re-initialization issues. This has been fixed to ensure that the parent process continues running even in such cases. (GitHub issue)
Ferron 3.0.0-beta.2
Released in June 14, 2026
Breaking changes
If you are upgrading to this beta version, you must update your configuration files to accommodate the following syntax refactors:
- Rate limit windows - syntax updated to enforce standard duration strings (e.g.,
10s,5m,1h). - OTLP verification -
no_verifyhas been renamed tono_verificationand now operates strictly as a configuration flag. - OTLP defaults - OTLP sink now uses
log_style moderndefault, which may break existing configurations with custom OTLP log formats. - Proxy configuration - syntax for passive/active health checks, load balancing algorithms, and connection retries has been unified into a cleaner, more consistent format.
- Incoming trace context discarded by default - Ferron no longer trusts incoming
traceparent,tracestate, orbaggageheaders unlesstrust_request trueis explicitly set in thetraceblock. Previously, incoming trace context was always parsed and used as the parent span. To restore the old behavior, addtrust_request trueto yourhttp { trace { ... } }block.
Added
Modules
http-abuseban- a new module for lightweight, native Fail2ban-like IP banning with temporary lockouts based on rate limit breaches and brute-force failures.http-traceid- a new module that injects the current request’s trace ID into HTTP response headers (configurable header name, optional on-demand reflection viaX-Ferron-Trace-Reflect).tls-http- support for obtaining TLS certificates from a remote HTTP endpoint, featuring automatic refresh cycles and dedicated observability metrics.- OS metrics - added Windows support for native process metrics in the
metrics-processmodule.
Reverse proxy & load balancing
circuit_breaker- new directive with rolling failure windows, temporary backend ejection, and half-open recovery states.p2c_ewma- a new adaptive, latency-aware load balancing algorithm combining Power of Two Choices with Exponentially Weighted Moving Average (EWMA) latency scoring.- Session affinity (sticky sessions) - native support for
cookie,header,ip, andhashtypes utilizing a Ketama-style hash ring for deterministic backend routing. - String interpolation - upstream URLs and Unix socket paths now support interpolated strings.
- SRV routing - added active health check support for SRV upstream URLs.
- Outbound mTLS - reverse proxy upstreams now support presenting client certificates (
certandkeysubdirectives) for mutual TLS authentication with HTTPS backends, including per-upstream credential scoping and SRV upstream support.
DNS & ACME
- 58 new DNS providers - native ACME DNS-01 challenge support expanded to include Alibaba Cloud DNS, Azure DNS, ClouDNS, Hetzner DNS, Oracle Cloud DNS, Vercel, Vultr, Yandex Cloud DNS, and 50+ others.
- CLI arguments in post-obtain command - added support for passing CLI arguments to the post-obtain command in automatic TLS configurations.
HTTP server core
basic_auth_concurrency- global directive to limit concurrent, resource-heavy password verification tasks across allbasic_authblocks.- Cache purging - native
PURGEHTTP method support for targeted cache invalidation via thepurge_methodandpurge_allowed_ipssubdirectives. force_trace- global directive to force trace context creation for every request even when tracing is not explicitly enabled by a module, useful for debugging or log correlation.trust_request- new directive in thetraceblock to accept incomingtraceparent,tracestate, andbaggageheaders as the parent trace context. Default:false. When disabled (default), incoming trace headers are discarded and Ferron generates a fresh trace ID for each request.- Optimal server-preferred compression algorithm - now supports automatic detection of the optimal compression algorithm based on client preferences (GitHub issue).
Authentication
- Forwarded authentication backend chaining - the forwarded authentication module now supports chaining multiple backends together, allowing for flexible and secure multi-backend authentication scenarios.
Observability & metrics
- Edge-case visibility - granular HTTP observability metrics for pre-handler failures, server redirects, client-IP rewrites, CORS preflights, connection lifecycle failures, forward-proxy outcomes, reverse-proxy failures, and static-file response outcomes.
- Admin sinks - added a dropped-events admin metric for non-blocking observability sinks.
- Unix file descriptor metrics - added
process.unix.file_descriptor.count(UpDownCounter) to track the change in number of unix file descriptors since the last measurement. (Linux only) - Backend exclusion tracking - new
ferron.proxy.backend.excludedcounter that records each exclusion event withbackend_urlandreasonattributes, covering passive-check failures, open circuit breakers, retry exclusion, and overloaded backends. - Retry observability - new
ferron.proxy.retry.countcounter andferron.proxy.retry.finalgauge to track retry attempts and whether the final attempt succeeded. - Connection pool metrics - new
ferron.proxy.pool.hitandferron.proxy.pool.misscounters, plusferron.proxy.pool.idleandferron.proxy.pool.outstandingworker-scoped gauges for per-upstream connection pool depth monitoring. - Connect latency & TTFB - new
ferron.proxy.connect.latencyandferron.proxy.ttfbhistograms measuring TCP connection establishment time and time to first response byte. - Health check instrumentation - new
ferron.proxy.health.success,ferron.proxy.health.failure, andferron.proxy.health.durationmetrics for active health probe outcomes. - Circuit breaker metrics - new
ferron.proxy.circuit.stategauge (0/1/2 for Closed/HalfOpen/Open) andferron.proxy.circuit.open_totalcounter tracking circuit breaker state transitions. - Unified certificate expiration gauge - a single
ferron.tls.certificate_not_aftergauge is emitted by every TLS provider (manual,acme,http,local) whenever a certificate is mounted into the in-memory rustls context. The value is the certificatenotAfterfield as Unix epoch seconds; attributes areferron.host(SNI hostname or IP),ferron.tls.provider(provider name), andcrypto.certificate.serial_number(lowercase hex). Replaces the previous provider-specific expiration gauges. - Baggage promotion - new
baggagesub-directive in observability backend blocks (otlp,prometheus) to promote specific W3C Baggage keys into telemetry attributes for logs, metrics, and traces. Supports per-signal filtering and amax_distinctcap to prevent high-cardinality metric label explosion. - Baggage propagation - Baggage is now propagated from incoming requests to outgoing ones, allowing distributed tracing context to be preserved across service boundaries.
- CGI/FastCGI/SCGI observability metrics - added request count, failure count, upstream duration, and stderr error metrics for the
http-fcgi,http-cgi, andhttp-scgimodules. FastCGI also exposes connection pool wait and upstream duration metrics; CGI exposes process duration and exit code metrics; SCGI exposes upstream duration metrics. log_styledirective - newlog_style legacy|moderndirective in the OTLP observability backend block to opt into OTEL-style structured logs. Inmodernmode, each log record publishes a shortsummarybody plus typed per-event attributes (string, bool, integer, float) instead of the human-readable message. Access logs are remapped to OTEL semantic conventions (url.path,http.request.method,http.response.status_code,client.address,http.server.request.duration, etc.). The defaultlegacymode preserves existing behavior.- Application log formatters via
error_format- theerror_logdirective andprovider filenow support anerror_formatsubdirective to choose how application (error) log messages are formatted. Thetextformatter (default) produces human-readable lines with timestamp, level, optional trace ID, and message. Thejsonformatter produces structured JSON records withsummary,level,target,attributes, andtrace_contextfields. Formatter implementations are provided by thelogformat-textandlogformat-jsonmodules respectively. - Logs for OTLP errors - Errors with the logs, metrics and traces providers are now logged.
Admin API
- Added
GET /reloadandGET /runtimeendpoints to the admin listener.
Configuration syntax
- Globs in
includedirectives -include "path.conf"now supports glob patterns, e.g.include "/etc/ferron/conf.d/**/*.conf".
Configuration validation
- Best-practice enforcement -
ferron doctorsubcommand for validating best practices across the configuration. - Print “all good” message - a message is now printed when the configuration is valid with no diagnostics.
- JSON configuration validation results are now printed to stdout when
--jsonis specified, improving observability for automated tools and CI/CD pipelines. - Unused subdirectives are now reported as well as directives.
Docker
- Entrypoint configuration - Docker images now include a dedicated entrypoint config with JSON console logging, trace ID forcing, and an
include /etc/ferron/conf.d/**/*.confdirective for user-provided split configurations.
Changed
Reverse proxy
- Weighted load balancing - both
least_connandround_robinalgorithms now support per-upstreamweightdirectives for proportional traffic distribution. - Metric attributes - multiple HTTP reverse proxy metrics now embed the specific backend URL or Unix socket path as a context attribute.
DNS & TLS
- Early OCSP verification - OCSP responses are now strictly verified before being cached and stapled.
- Verbose errors - significantly improved error reporting layouts for local automatic TLS and specific TLS handshake failures.
/var/cache/ferron-acmeprioritized over user’s cache dir - for Docker images with Ferron running asroot, the ACME cache directory is now/var/cache/ferron-acme, which takes precedence over user’s ACME cache directory.
HTTP server core
- Safe configuration reloads - configuration failures during a live reload no longer crash or stop the server; errors are logged, and execution safely continues using the previous valid configuration.
- Smarter brute-force locking - protection now locks by IP address instead of username, preventing malicious actors from intentionally locking out legitimate users.
- Accurate status codes - refactored error handling to return context-aware status codes over generic
500or404errors:- File pipeline returns
408 Request Timeoutif a request takes too long (instead of404). - Basic Auth returns
429 Too Many Requestswhen max failed attempts are reached. - File serving errors return
403 Forbidden(permissions) or400 Bad Request(invalid filenames) instead of500.
- File pipeline returns
- Support for multi-range partial static file serving - now supports serving multiple ranges from a single request, improving efficiency and reducing latency.
- String interpolation - forwarded authentication now supports interpolated string values for backend URLs.
- Security tightening - URL canonicalization now strictly rejects paths containing null bytes (
\0or%00). - Cache cleanliness -
X-LiteSpeed-Cacheheaders are no longer emitted by default; can be re-enabled via theemit_litespeed_headerssubdirective.
Observability & tracing
- Unified request tracing - HTTP tracing now rolls up into a single
ferron.requestroot span with nested child spans for pipelines, stages, file-serving, and error pipelines. - Log correlation - OTLP request and access logs now include the active request span context out of the box.
- Backend exporting - admin API metrics are now also pushed directly to configured observability backends, not just the local admin endpoint.
- Cardinality control - Prometheus label values are now sanitized to heavily reduce high-cardinality label inflation.
- Structured log events - every log emission site now carries a short OTEL-friendly
summaryplus typedattributes, enabling downstream OTLP consumers to receive structured events without changing existing console or file log output. - Trace ID in console and file logs - console and file loggers now prefix log messages with
[trace=<span_id>]when a trace context is available, enabling grep-based filtering by trace ID. - Trace context injection for CGI/FastCGI/PHP-FPM/SCGI - trace context headers are now automatically propagated to the backend environment via CGI/FastCGI/PHP-FPM/SCGI.
- Incoming trace context discarded by default - Ferron now generates a fresh trace ID for every request by default, discarding incoming
traceparent/tracestate/baggageheaders unlesstrust_request trueis configured. Existing deployments relying on incoming trace context must addtrust_request trueto thetraceblock. - Sanitized trace header injection -
inject_trace_headersnow removes any existingtraceparent,tracestate, andbaggageheaders before injecting new values, preventing header duplication or conflicts.
Core runtime
- Unified durations - improved configuration-wide consistency for duration formatting values.
- Graceful shutdown - the server process now handles standard Unix
SIGTERMsignals for seamless graceful shutdowns. - Frictionless local TLS - the server now issues a clean warning if local automatic TLS is configured but the cache directory isn’t writable, instead of refusing to boot.
Docker
- Split configuration layout - Docker images now use a two-file layout:
/etc/ferron/ferron.confas the entrypoint config and/etc/ferron/conf.d/for user configuration files. Docker Compose bind mounts now target theconf.d/directory.
Package configuration
- Log rotation and structured logging - Default package configurations now include log rotation settings and commented-out structured logging and trace ID format options.
Fixed
Reverse proxy
- Fixed a bug where pool limits weren’t being respected when pulling connections from the pool, leading to possible handle exhaustion.
- Fixed a bug where reverse proxy boolean subdirectives with empty values (implying
true) were being ignored. - Fixed an issue where the proxy failed to strip headers specified by the
Connectionheader per RFC 7230.
DNS & ACME
- Fixed an “invalid socket address” error that broke RFC 2136 dynamic DNS updates for the ACME DNS-01 challenge.
- Resolved an edge case where OCSP stapling failed to immediately fetch the response after new certificates were registered.
HTTP server core
- Auth bypass closed - fixed a critical flaw where a misconfigured forwarded authentication block could result in bypassing authentication entirely.
- Cache thundering herd fixed - implemented request coalescing for cache misses to prevent thundering herd scenarios when multiple requests hit the same uncached resource simultaneously.
- Several HTTP/1.x protocol handling bugs fixed - fixed chunked-length encoding DoS vulnerability and other protocol-related issues (see
vibeio-httpchangelog). - Fixed a
500 Internal Server Errorwhen using theauth_to { ... }syntax inside forwarded authentication blocks. - Fixed a bug where case-insensitive HTTP cache control directives were not recognized correctly.
- Fixed a bug where CONNECT requests with authority-form URIs were erroneously blocked by the URL canonicalizer.
- Fixed a bug where HTTP connections couldn’t be accepted on 32-bit Windows (see
vibeiochangelog; GitHub issue). - Fixed a bug where HTTP connections were accepted by only one OS thread on Windows.
- Fixed a bug where HTTP timeout durations were not being respected correctly.
- Fixed a bug where TLS certificate resolver from domain name level higher (non-wildcard) was incorrectly used for TLS handshakes if the one from the domain name level matching the requested SNI isn’t present.
- Fixed HTTP-to-HTTPS redirects to correctly target the original requested URL rather than internal rewritten URLs.
Observability
- Log injection fixed - implemented strict sanitization of log fields to prevent potential log injection attacks for plain-text logs via malicious header values or other user input.
- Fixed a data blind spot where malformed and timed-out requests rejected before normal handler completion went uncounted by the observability pipeline.
- The server now removes sensitive HTTP headers from access logs (such as
cookie,authorization), to prevent sensitive data exposure in log output. - Corrected inaccurate memory metrics that were calculating values relative to initial memory usage instead of absolute usage.
Security
- DNS rebinding fixed - closed a vulnerability where forward-proxy DNS validation could be bypassed via a race condition combined with a DNS rebinding attack.
- Fixed an issue where forward-proxy allowed ports and denied IP addresses were being treated as additive rather than strictly respecting user configuration overrides.
Runtime operations
- Fixed an Admin API-initiated reload loop that caused infinite configuration reload loops.
- Fixed admin API over-redacting configuration directives in
/configendpoint responses, which could lead to confusion when verifying configuration values. - Eliminated a rate limiting race condition when initializing a brand new key bucket, which previously allowed traffic to briefly exceed configured capacities.
- Fixed manual TLS session ticket key rotation to properly read from configured key files instead of silently falling back to in-memory generation.
- Fixed a bug on Linux where
io_uringcould not be explicitly disabled through the server configuration file. - Fixed an edge case where cached responses replaced by non-cached default error pages could be returned stale.
Ferron 3.0.0-beta.1
Released in May 5, 2026
Added
- CLI utility for hashing passwords.
- CLI utility for pre-compressing static files.
- CLI utility for translating Ferron 2 configurations into Ferron 3 ones.
- CLI utility for zero-configuration serving.
Changed
- Non-existent webroots now lead to 404 Not Found errors instead of 500 Internal Server Error errors.
Fixed
- Partial hostname resolution match in HTTP server could lead to incorrect routing.
- Redirects configured with
statusdirective didn’t have some placeholder locations (such as$1) replaced when using a regex match. - Redirects configured with
statusdirective didn’t lead to any destination. - Reverse proxy was sometimes routed to wrong backend server.
- Some default cache paths were unwritable in some cases.
- Unknown directives in global blocks for
statusdirective (even though they’re known in host blocks) caused the web server to fail to start. - When using OTLP, access logs were emitted with “access_log” body, not actual access logs.
Ferron 3.0.0-alpha.3
Released in April 23, 2026
Added
- Multiple DNS providers for DNS-01 ACME challenge.
- Support for CGI (Common Gateway Interface).
- Support for FastCGI (including PHP-FPM).
- Support for forwarded authentication.
- Support for SCGI (Simple Common Gateway Interface).
Fixed
- Connections to HTTP/2 backends in reverse proxy were aborted.
- Error interception configuration blocks weren’t applied properly.
- HTTP to HTTPS redirect wasn’t enabled by default.
- HTTP upgrades weren’t performed properly.
- HTTP-01 ACME challenge failed due to challenge not being served for implicit automatic TLS.
- On-demand automatic TLS wasn’t functioning correctly.
- Some ACME events were logged only to the console, not observability backends.
- TLS certificates with local CA weren’t checked if they’re expired.
Ferron 3.0.0-alpha.2
Released in April 17, 2026
Added
- Active health checking in reverse proxy support.
- Automatic TLS with local certificate authority (CA).
- Experimental HTTP/3 support.
mapdirective for mapping variables.- Prometheus metrics export support.
- Response body string replacement support.
- Support for body interpolation in
statusdirectives. - Support for interpolated strings in header values.
- W3C Trace Context (traceparent and tracestate) propagation and generation.
Changed
- Improved the request URL normalization.
- Requests with multiple Host headers are now rejected.
Fixed
- PROXY protocol setting, connection retry setting and error interception weren’t working for reverse proxy.
- Zerocopy static file serving wasn’t working properly on Linux, because it wasn’t enabled.
Ferron 3.0.0-alpha.1
Released in April 10, 2026
Changed
- First alpha release of Ferron 3.