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-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.